diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Bruno Dias (c) 2018
+
+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 Author name here 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/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -0,0 +1,4 @@
+# 0.3.0
+
+- fixed parsing escaped strings.
+- now using cabal with nix.
diff --git a/language-js.cabal b/language-js.cabal
--- a/language-js.cabal
+++ b/language-js.cabal
@@ -1,58 +1,50 @@
--- This file has been generated from package.yaml by hpack version 0.21.2.
---
--- see: https://github.com/sol/hpack
---
--- hash: f70584b078bd494fad79a4ae3eb3040e96ec6c49e2ef67cc8774889e5827bafc
+cabal-version:      3.0
+name:               language-js
+version:            0.3.0
 
-name:           language-js
-version:        0.2.0
-synopsis:       javascript parser for es6 and es7.
-description:    Please see the README on Github at <https://github.com/diasbruno/language-js#README.md>
-category:       Language
-homepage:       https://github.com/diasbruno/language-js#readme
-bug-reports:    https://github.com/diasbruno/language-js/issues
-author:         Bruno Dias <dias.h.bruno@gmail.com>
-maintainer:     Bruno Dias <dias.h.bruno@gmail.com>
-copyright:      2018 Bruno Dias
-license:        BSD3
-license-file:   license.md
-build-type:     Simple
-cabal-version:  >= 1.10
+synopsis:           javascript parser for es6 and es7.
+description:        javascript parser for es6 and es7.
 
+bug-reports:        https://github.com/diasbruno/language-js/issues
+
+category:           Language
+license:            MIT
+license-file:       LICENSE
+author:             Bruno Dias
+maintainer:         dias.h.bruno@gmail.com
+
 extra-source-files:
     changelog.md
     readme.md
 
-source-repository head
-  type: git
-  location: https://github.com/diasbruno/language-js
-
 library
-  exposed-modules:
-      Language.JS.Common
-      Language.JS.Operators
-      Language.JS.Parser
-      Language.JS.Types
-  other-modules:
-      Paths_language_js
-  hs-source-dirs:
-      src
-  build-depends:
-      base >=4.7 && <5
-    , parsec
-  default-language: Haskell2010
+    default-language: Haskell2010
+    build-depends:    base <5
+                    , parsec ==3.1.14.0
+    hs-source-dirs:   src
+    exposed-modules:  Language.JS.Common
+                    , Language.JS.Operators
+                    , Language.JS.Parser
+                    , Language.JS.Types
 
-test-suite language-js-test
-  type: exitcode-stdio-1.0
-  main-is: Spec.hs
-  other-modules:
-      Paths_language_js
-  hs-source-dirs:
-      test
-  ghc-options: -threaded -rtsopts -with-rtsopts=-N
-  build-depends:
-      base >=4.7 && <5
-    , hspec
-    , language-js
-    , parsec
-  default-language: Haskell2010
+test-suite tests
+    default-language: Haskell2010
+    type:             exitcode-stdio-1.0
+    hs-source-dirs:   t
+    main-is:          Spec.hs
+    other-modules:    Test.AssignmentAndOperation
+                    , Test.Classes
+                    , Test.Exports
+                    , Test.Functions
+                    , Test.Helpers
+                    , Test.If
+                    , Test.Imports
+                    , Test.Iteration
+                    , Test.MemberExpression
+                    , Test.Switch
+                    , Test.Try
+                    , Test.Variables
+    build-depends:    base
+                    , hspec
+                    , parsec
+                    , language-js
diff --git a/license.md b/license.md
deleted file mode 100644
--- a/license.md
+++ /dev/null
@@ -1,30 +0,0 @@
-Copyright Bruno Dias (c) 2018
-
-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 Author name here 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/src/Language/JS/Parser.hs b/src/Language/JS/Parser.hs
--- a/src/Language/JS/Parser.hs
+++ b/src/Language/JS/Parser.hs
@@ -57,13 +57,26 @@
 
 -- | Parse string literal.
 stringLiteral =
-  buildExpression LS '\"' withoutNewLineAllowed
-  <|> buildExpression LS '\'' withoutNewLineAllowed
+  buildExpression '"'
+  <|> buildExpression '\''
   <|> LTS <$> (P.char '`' *> templateString "" [])
   P.<?> "[string-literal]"
   where
-    withoutNewLineAllowed e = P.many (P.satisfy (\c -> c /= '\n' && c /= e))
-    buildExpression ctor wc p = ctor <$> P.try (P.between (P.char wc) (P.char wc) (p wc))
+    buildExpression wc = do
+      P.char wc
+      content <- P.many (escaped <|> P.noneOf [wc, '\n'])
+      P.char wc
+      return $ LS content
+    escaped = do
+      P.char '\\'
+      P.choice $ zipWith escapedChar codes replacements
+    escapedChar code replacement = do
+      P.char code
+      return replacement
+    codes        = ['b',  'n',  'f',  'r',  't',  '\\', '\"', '\'']
+    replacements = ['\b', '\n', '\f', '\r', '\t', '\\', '\"', '\'']
+
+
 
 -- | Parse template strings.
 templateString str ls = (do
diff --git a/t/Spec.hs b/t/Spec.hs
new file mode 100644
--- /dev/null
+++ b/t/Spec.hs
@@ -0,0 +1,240 @@
+module Main where
+
+import Control.Monad (when)
+import System.Exit
+import Test.Hspec
+import Test.Hspec.Runner
+import Test.Helpers
+import Test.MemberExpression
+import Test.AssignmentAndOperation
+import Test.Variables
+import Test.If
+import Test.Try
+import Test.Switch
+import Test.Functions
+import Test.Classes
+import Test.Iteration
+import Test.Exports
+import Test.Imports
+
+testExpressions :: Spec
+testExpressions = describe "Parse literals:" $ do
+  it "this" $ do
+    shouldBe (testExpression "this")
+      "Right LThis"
+
+  it "undefined/null" $ do
+    shouldBe (testExpression "null")
+      "Right LNull"
+
+  it "booleans" $ do
+    shouldBe (testExpression "true")
+      "Right (LB True)"
+
+    shouldBe (testExpression "false")
+      "Right (LB False)"
+
+  it "ident" $ do
+    shouldBe (testExpression "test")
+      "Right (LI \"test\")"
+
+  it "numbers" $ do
+    shouldBe (testExpression "1")
+      "Right (LN \"1\")"
+
+    shouldBe (testExpression "1.10")
+      "Right (LN \"1.10\")"
+
+    shouldBe (testExpression "0.10e3")
+      "Right (LN \"0.10e3\")"
+
+    shouldBe (testExpression "0x11")
+      "Right (LN \"0x11\")"
+
+    shouldBe (testExpression "0b11")
+      "Right (LN \"0b11\")"
+
+  it "strings" $ do
+    shouldBe (testExpression "\"test\"")
+      "Right (LS \"test\")"
+    shouldBe (testExpression "'test'")
+      "Right (LS \"test\")"
+    shouldBe (testExpression "'\\''")
+      "Right (LS \"'\")"
+    shouldBe (testExpression "'\\n'")
+      "Right (LS \"\\n\")"
+    shouldBe (testExpression "'\n'")
+      "Left \"dummy.js\" (line 1, column 2):\nunexpected \"\\n\"\nexpecting \"\\\\\" or \"'\""
+
+  it "template string" $ do
+    shouldBe (testExpression "`test`")
+      "Right (LTS [TString \"test\"])"
+
+    shouldBe (testExpression "`${\"a\"}`")
+      "Right (LTS [TExpression (LS \"a\")])"
+
+    shouldBe (testExpression "`test ${a + 1} test`")
+      "Right (LTS [TString \"test \",TExpression (Operation \"+\" (LI \"a\") (LN \"1\")),TString \" test\"])"
+
+    shouldBe (testExpression "`${a} test ${b}`")
+      "Right (LTS [TExpression (LI \"a\"),TString \" test \",TExpression (LI \"b\")])"
+
+  it "arrays" $ do
+    shouldBe (testExpression "[]")
+      "Right (LA [])"
+
+    shouldBe (testExpression "[,]")
+      "Right (LA [Elision])"
+
+    shouldBe (testExpression "[1,2]")
+      "Right (LA [LN \"1\",LN \"2\"])"
+
+    shouldBe (testExpression "[a,2]")
+      "Right (LA [LI \"a\",LN \"2\"])"
+
+    shouldBe (testExpression "[a,...b]")
+      "Right (LA [LI \"a\",Spread (LI \"b\")])"
+
+    shouldBe (testExpression "[a,...[1, 2]]")
+      "Right (LA [LI \"a\",Spread (LA [LN \"1\",LN \"2\"])])"
+
+    shouldBe (testExpression "[a,,b]")
+      "Right (LA [LI \"a\",Elision,LI \"b\"])"
+
+  it "objects" $ do
+    shouldBe (testExpression "{}")
+      "Right (LO [])"
+
+    shouldBe (testExpression "{a:1}")
+      "Right (LO [OPKV (LI \"a\") (LN \"1\")])"
+
+    shouldBe (testExpression "{b:2,c: \"d\"}")
+      "Right (LO [OPKV (LI \"b\") (LN \"2\"),OPKV (LI \"c\") (LS \"d\")])"
+
+    shouldBe (testExpression "{b:2, c:function() {}}")
+      "Right (LO [OPKV (LI \"b\") (LN \"2\"),OPKV (LI \"c\") (Function Nothing [] (SBlock []))])"
+
+    shouldBe (testExpression "{e}")
+      "Right (LO [OPI (LI \"e\")])"
+
+    shouldBe (testExpression "{e,f:1}")
+      "Right (LO [OPI (LI \"e\"),OPKV (LI \"f\") (LN \"1\")])"
+
+    shouldBe (testExpression "{e,...f}")
+      "Right (LO [OPI (LI \"e\"),OPI (Spread (LI \"f\"))])"
+
+    shouldBe (testExpression "{e,...{a:1}}")
+      "Right (LO [OPI (LI \"e\"),OPI (Spread (LO [OPKV (LI \"a\") (LN \"1\")]))])"
+
+    shouldBe (testExpression "{e,i:...{a:1}}")
+      "Right (LO [OPI (LI \"e\"),OPKV (LI \"i\") (Spread (LO [OPKV (LI \"a\") (LN \"1\")]))])"
+
+    shouldBe (testExpression "{e(){}}")
+      "Right (LO [OPM (PropertyMethod (LI \"e\") [] (SBlock []))])"
+
+    shouldBe (testExpression "{get e(){}}")
+      "Right (LO [OPM (ClassGetMethod (PropertyMethod (LI \"e\") [] (SBlock [])))])"
+
+    shouldBe (testExpression "{set e(){}}")
+      "Right (LO [OPM (ClassSetMethod (PropertyMethod (LI \"e\") [] (SBlock [])))])"
+
+    shouldBe (testExpression "{async e(){}}")
+      "Right (LO [OPM (Async (PropertyMethod (LI \"e\") [] (SBlock [])))])"
+
+  it "regular expression" $ do
+    shouldBe (testExpression "/test\\/asdf/")
+      "Right (RegExp \"test\\\\/asdf\" \"\")"
+
+    shouldBe (testExpression "/test\\/asdf/gmi")
+      "Right (RegExp \"test\\\\/asdf\" \"gmi\")"
+
+    shouldBe (testExpression "/test\\/asdf/gmi.exec(test)")
+      "Right (FCall (Dot (RegExp \"test\\\\/asdf\" \"gmi\") (LI \"exec\")) [LI \"test\"])"
+
+  it "parens expression" $ do
+    shouldBe (testExpression "(a,b)")
+      "Right (LP (Comma (LI \"a\") (LI \"b\")))"
+
+  it "new expression" $ testNewExpression
+
+  it "function call expression" $ testFunctionCall
+
+  it "dotted accesor" $ testDotAccessor
+
+  it "array accessor" $ testArrayAccessor
+
+  it "function expression" $ testFunctions
+
+  it "class expression" $ testClasses
+
+  it "unary expression" $ testUnaryOperations
+
+  it "assignment expression" $ testAssignments
+
+  it "operation expressions" $ testOperations
+
+  it "spread expression" $ do
+    shouldBe (testExpression "{...a}")
+      "Right (LO [OPI (Spread (LI \"a\"))])"
+
+  it "empty expression" $ do
+    shouldBe (testExpression ";")
+      "Right Empty"
+
+testStatements = describe "Statements" $ do
+  it "empty statement" $ do
+    shouldBe (testStatement ";")
+      "Right (SExp Empty)"
+
+  it "if statement" $ testIf
+
+  it "try statement" $ testTry
+
+  it "throw statements" $ do
+    shouldBe (testStatement "throw x;")
+      "Right (SThrow (LI \"x\"))"
+
+  it "variable statements" $ testVariables
+
+  it "switch statement" $ testSwitch
+
+  it "continue statement" $ do
+    shouldBe (testStatement "continue")
+      "Right (SContinue Nothing)"
+
+    shouldBe (testStatement "continue x")
+      "Right (SContinue (Just (LI \"x\")))"
+
+  it "debugger statement" $ do
+    shouldBe (testStatement "debugger")
+      "Right SDebugger"
+
+  it "iteration statement" $ testIteration
+
+  it "with statement" $ do
+    shouldBe (testStatement "with (a) {}")
+      "Right (SWith (LI \"a\") (SBlock []))"
+
+  it "labelled statement" $ do
+    shouldBe (testStatement "a: x = 1")
+      "Right (SLabel (LI \"a\") (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
+
+  it "import statement" $ testImports
+
+  it "export statement" $ testExports
+
+  it "function declaration" $ testFunctionDeclaration
+
+  it "class declaration" $ testClassesDeclaration
+
+testAll :: Spec
+testAll = do
+  testExpressions
+  testStatements
+
+main :: IO ()
+main = do
+  summary <- hspecWithResult defaultConfig testAll
+  when (summaryFailures summary == 0)
+    exitSuccess
+  exitFailure
diff --git a/t/Test/AssignmentAndOperation.hs b/t/Test/AssignmentAndOperation.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/AssignmentAndOperation.hs
@@ -0,0 +1,133 @@
+module Test.AssignmentAndOperation where
+
+import Test.Hspec
+import Test.Helpers
+
+testUnaryOperations = do
+    shouldBe (testExpression "+a")
+      "Right (Unary \"+\" (LI \"a\"))"
+
+    shouldBe (testExpression "-a")
+      "Right (Unary \"-\" (LI \"a\"))"
+
+    shouldBe (testExpression "~a")
+      "Right (Unary \"~\" (LI \"a\"))"
+
+    shouldBe (testExpression "!a")
+      "Right (Unary \"!\" (LI \"a\"))"
+
+    shouldBe (testExpression "delete a")
+      "Right (Unary \"delete\" (LI \"a\"))"
+
+    shouldBe (testExpression "typeof a")
+      "Right (Unary \"typeof\" (LI \"a\"))"
+
+    shouldBe (testExpression "void a")
+      "Right (Unary \"void\" (LI \"a\"))"
+
+    shouldBe (testExpression "++a")
+      "Right (UnaryUpdate \"++\" True (LI \"a\"))"
+
+    shouldBe (testExpression "a++")
+      "Right (UnaryUpdate \"++\" False (LI \"a\"))"
+
+    shouldBe (testExpression "--a")
+      "Right (UnaryUpdate \"--\" True (LI \"a\"))"
+
+    shouldBe (testExpression "a--")
+      "Right (UnaryUpdate \"--\" False (LI \"a\"))"
+
+testAssignments = do
+    shouldBe (testExpression "x = 1")
+      "Right (Assignment \"=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x += 1")
+      "Right (Assignment \"+=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x -= 1")
+      "Right (Assignment \"-=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x *= 1")
+      "Right (Assignment \"*=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x /= 1")
+      "Right (Assignment \"/=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x %= 1")
+      "Right (Assignment \"%=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x &= 1")
+      "Right (Assignment \"&=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x |= 1")
+      "Right (Assignment \"|=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x **= 1")
+      "Right (Assignment \"**=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x >>= 1")
+      "Right (Assignment \">>=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x <<= 1")
+      "Right (Assignment \"<<=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x >>>= 1")
+      "Right (Assignment \">>>=\" (LI \"x\") (LN \"1\"))"
+
+    shouldBe (testExpression "x.a += 1")
+      "Right (Assignment \"+=\" (Dot (LI \"x\") (LI \"a\")) (LN \"1\"))"
+
+testOperations = do
+     shouldBe (testExpression "a + b")
+       "Right (Operation \"+\" (LI \"a\") (LI \"b\"))"
+
+     shouldBe (testExpression "a - b")
+       "Right (Operation \"-\" (LI \"a\") (LI \"b\"))"
+
+     shouldBe (testExpression "a * b")
+       "Right (Operation \"*\" (LI \"a\") (LI \"b\"))"
+
+     shouldBe (testExpression "a / b")
+       "Right (Operation \"/\" (LI \"a\") (LI \"b\"))"
+
+     shouldBe (testExpression "a = b - c")
+       "Right (Assignment \"=\" (LI \"a\") (Operation \"-\" (LI \"b\") (LI \"c\")))"
+
+     shouldBe (testExpression "a + b - c")
+       "Right (Operation \"-\" (Operation \"+\" (LI \"a\") (LI \"b\")) (LI \"c\"))"
+
+     shouldBe (testExpression "a + x.a - x.c")
+       "Right (Operation \"-\" (Operation \"+\" (LI \"a\") (Dot (LI \"x\") (LI \"a\"))) (Dot (LI \"x\") (LI \"c\")))"
+
+     shouldBe (testExpression "a + x.a && x.c")
+       "Right (Operation \"&&\" (Operation \"+\" (LI \"a\") (Dot (LI \"x\") (LI \"a\"))) (Dot (LI \"x\") (LI \"c\")))"
+
+     shouldBe (testExpression "a == x.a + x.c")
+       "Right (Operation \"==\" (LI \"a\") (Operation \"+\" (Dot (LI \"x\") (LI \"a\")) (Dot (LI \"x\") (LI \"c\"))))"
+
+     shouldBe (testExpression "!a")
+       "Right (Unary \"!\" (LI \"a\"))"
+
+     shouldBe (testExpression "a = x.a && x.c")
+       "Right (Assignment \"=\" (LI \"a\") (Operation \"&&\" (Dot (LI \"x\") (LI \"a\")) (Dot (LI \"x\") (LI \"c\"))))"
+
+     shouldBe (testExpression "a instanceof c")
+       "Right (Operation \"instanceof\" (LI \"a\") (LI \"c\"))"
+
+     shouldBe (testExpression "typeof a == \"number\"")
+       "Right (Operation \"==\" (Unary \"typeof\" (LI \"a\")) (LS \"number\"))"
+
+     shouldBe (testExpression "x ? y : z")
+       "Right (Condition (LI \"x\") (LI \"y\") (LI \"z\"))"
+
+     shouldBe (testExpression "x == 1 ? y : z")
+       "Right (Condition (Operation \"==\" (LI \"x\") (LN \"1\")) (LI \"y\") (LI \"z\"))"
+
+     shouldBe (testExpression "a = x == 1 ? y + 1 : z && 6")
+       "Right (Assignment \"=\" (LI \"a\") (Condition (Operation \"==\" (LI \"x\") (LN \"1\")) (Operation \"+\" (LI \"y\") (LN \"1\")) (Operation \"&&\" (LI \"z\") (LN \"6\"))))"
+
+     shouldBe (testExpression "a.b() || x")
+       "Right (Operation \"||\" (FCall (Dot (LI \"a\") (LI \"b\")) []) (LI \"x\"))"
+
+     shouldBe (testExpression "(x)\n.y")
+       "Right (Dot (LP (LI \"x\")) (LI \"y\"))"
diff --git a/t/Test/Classes.hs b/t/Test/Classes.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Classes.hs
@@ -0,0 +1,40 @@
+module Test.Classes where
+
+import Test.Helpers
+import Test.Hspec
+
+testClasses = do
+  shouldBe (testExpression "class {}")
+    "Right (Class Nothing Nothing (SBlock []))"
+
+  shouldBe (testExpression "class test {}")
+    "Right (Class (Just (LI \"test\")) Nothing (SBlock []))"
+
+  shouldBe (testExpression "class test extends A {}")
+    "Right (Class (Just (LI \"test\")) (Just (LI \"A\")) (SBlock []))"
+
+  shouldBe (testExpression "class extends A {}")
+    "Right (Class Nothing (Just (LI \"A\")) (SBlock []))"
+
+  shouldBe (testExpression "class extends A { x = 1 }")
+    "Right (Class Nothing (Just (LI \"A\")) (SBlock [SExp (ClassProperty (LI \"x\") (LN \"1\"))]))"
+
+  shouldBe (testExpression "class extends A { x() {} }")
+    "Right (Class Nothing (Just (LI \"A\")) (SBlock [SExp (PropertyMethod (LI \"x\") [] (SBlock []))]))"
+
+  shouldBe (testExpression "class { static x = 1 }")
+    "Right (Class Nothing Nothing (SBlock [SExp (ClassStatic (ClassProperty (LI \"x\") (LN \"1\")))]))"
+
+  shouldBe (testExpression "class { set x() { return 1 } }")
+    "Right (Class Nothing Nothing (SBlock [SExp (ClassSetMethod (PropertyMethod (LI \"x\") [] (SBlock [SReturn (LN \"1\")])))]))"
+
+  shouldBe (testExpression "class { get x() { return 1 } }")
+    "Right (Class Nothing Nothing (SBlock [SExp (ClassGetMethod (PropertyMethod (LI \"x\") [] (SBlock [SReturn (LN \"1\")])))]))"
+
+  shouldBe (testExpression "class { async x() { return 1 } }")
+    "Right (Class Nothing Nothing (SBlock [SExp (Async (PropertyMethod (LI \"x\") [] (SBlock [SReturn (LN \"1\")])))]))"
+
+
+testClassesDeclaration = do
+  shouldBe (testTopLevelStatement "class a {}")
+    "Right (SC \"a\" Nothing (SBlock []))"
diff --git a/t/Test/Exports.hs b/t/Test/Exports.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Exports.hs
@@ -0,0 +1,17 @@
+module Test.Exports where
+
+import Test.Hspec
+import Test.Helpers
+
+testExports = do
+  shouldBe (testTopLevelStatement "export {}")
+    "Right (SExport (SBlock []))"
+
+  shouldBe (testTopLevelStatement "export default {}")
+    "Right (SExportDefault (LO []))"
+
+  shouldBe (testTopLevelStatement "export {} from \"test.js\"")
+    "Right (SRExport (LO []) (LS \"test.js\"))"
+
+  shouldBe (testTopLevelStatement "export const a = 1;")
+    "Right (SExport (SVariable \"const\" [BindVar (LI \"a\") (Just (LN \"1\"))]))"
diff --git a/t/Test/Functions.hs b/t/Test/Functions.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Functions.hs
@@ -0,0 +1,42 @@
+module Test.Functions where
+
+import Test.Hspec
+import Test.Helpers
+
+testFunctions = do
+  shouldBe (testExpression "function a() {}")
+    "Right (Function (Just (LI \"a\")) [] (SBlock []))"
+
+  shouldBe (testExpression "function() {}")
+    "Right (Function Nothing [] (SBlock []))"
+
+  shouldBe (testExpression "function(a, ...b) {}")
+    "Right (Function Nothing [BindVar (LI \"a\") Nothing,BindRest (LI \"b\")] (SBlock []))"
+
+  shouldBe (testExpression "function(a=1) {}")
+    "Right (Function Nothing [BindVar (LI \"a\") (Just (LN \"1\"))] (SBlock []))"
+
+  shouldBe (testExpression "function(a=1) { return 1; }")
+    "Right (Function Nothing [BindVar (LI \"a\") (Just (LN \"1\"))] (SBlock [SReturn (LN \"1\")]))"
+
+  shouldBe (testExpression "function(a=1) {\n   function b() {} }")
+    "Right (Function Nothing [BindVar (LI \"a\") (Just (LN \"1\"))] (SBlock [SF \"b\" [] (SBlock [])]))"
+
+  shouldBe (testExpression "()=>x")
+    "Right (Arrow (Right []) (SExp (LI \"x\")))"
+
+  shouldBe (testExpression "()=>{x}")
+    "Right (Arrow (Right []) (SBlock [SExp (LI \"x\")]))"
+
+  shouldBe (testExpression "x=>{x}")
+    "Right (Arrow (Left (BindVar (LI \"x\") Nothing)) (SBlock [SExp (LI \"x\")]))"
+
+testFunctionDeclaration = do
+  shouldBe (testTopLevelStatement "function a() {} function b() {}")
+    "Right (SF \"a\" [] (SBlock []))"
+
+  shouldBe (testTopLevelStatement "function a() { var i; return 1; }")
+    "Right (SF \"a\" [] (SBlock [SVariable \"var\" [BindVar (LI \"i\") Nothing],SReturn (LN \"1\")]))"
+
+  shouldBe (testTopLevelStatement "function a() { function b() { return () => 1; } return 1; }")
+    "Right (SF \"a\" [] (SBlock [SF \"b\" [] (SBlock [SReturn (Arrow (Right []) (SExp (LN \"1\")))]),SReturn (LN \"1\")]))"
diff --git a/t/Test/Helpers.hs b/t/Test/Helpers.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Helpers.hs
@@ -0,0 +1,11 @@
+module Test.Helpers where
+
+import Language.JS.Parser hiding (parse)
+import qualified Language.JS.Parser as JS
+import Text.Parsec
+
+testExpression = parse expressions "dummy.js" >>= return . show
+testStatement = parse statements "dummy.js" >>= return . show
+testTopLevelStatement = parse topLevelStatements "dummy.js" >>= return . show
+
+testParseJs = JS.parse "dummy.js" >>= return . show
diff --git a/t/Test/If.hs b/t/Test/If.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/If.hs
@@ -0,0 +1,20 @@
+module Test.If where
+
+import Test.Hspec
+import Test.Helpers
+
+testIf = do
+  shouldBe (testStatement "if (1) x = 1")
+    "Right (SIf (LP (LN \"1\")) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))) Nothing)"
+
+  shouldBe (testStatement "if (1)\nx = 1")
+    "Right (SIf (LP (LN \"1\")) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))) Nothing)"
+
+  shouldBe (testStatement "if (1) {x = 1}")
+    "Right (SIf (LP (LN \"1\")) (SBlock [SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))]) Nothing)"
+
+  shouldBe (testStatement "if (1) {x = 1} else x = 2")
+    "Right (SIf (LP (LN \"1\")) (SBlock [SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))]) (Just (SExp (Assignment \"=\" (LI \"x\") (LN \"2\")))))"
+
+  shouldBe (testStatement "if (a in [1, 2]) {}")
+    "Right (SIf (LP (Operation \"in\" (LI \"a\") (LA [LN \"1\",LN \"2\"]))) (SBlock []) Nothing)"
diff --git a/t/Test/Imports.hs b/t/Test/Imports.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Imports.hs
@@ -0,0 +1,17 @@
+module Test.Imports where
+
+import Test.Hspec
+import Test.Helpers
+
+testImports = do
+  shouldBe (testTopLevelStatement "import \"test.js\"")
+    "Right (SImportFile (LS \"test.js\"))"
+
+  shouldBe (testTopLevelStatement "import * as A from \"test.js\"")
+    "Right (SImport (Left (Namespace (LI \"A\"))) (LS \"test.js\"))"
+
+  shouldBe (testTopLevelStatement "import {x} from \"test.js\"")
+    "Right (SImport (Right [BindNames [LI \"x\"]]) (LS \"test.js\"))"
+
+  shouldBe (testTopLevelStatement "import B, {x} from \"test.js\"")
+    "Right (SImport (Right [DefaultName (LI \"B\"),BindNames [LI \"x\"]]) (LS \"test.js\"))"
diff --git a/t/Test/Iteration.hs b/t/Test/Iteration.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Iteration.hs
@@ -0,0 +1,60 @@
+module Test.Iteration where
+
+import Test.Hspec
+import Test.Helpers
+
+testIteration = do
+  shouldBe (testStatement "while (1) {}")
+    "Right (SWhile [LN \"1\"] (SBlock []))"
+
+  -- break is allowed here
+  shouldBe (testStatement "while (1) {break}")
+    "Right (SWhile [LN \"1\"] (SBlock [SBreak Nothing]))"
+
+  shouldBe (testStatement "while (1) {\nbreak\n;}")
+    "Right (SWhile [LN \"1\"] (SBlock [SBreak Nothing]))"
+
+  shouldBe (testStatement "while (1) x = 1")
+    "Right (SWhile [LN \"1\"] (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
+
+  shouldBe (testStatement "do {} while (1)")
+    "Right (SDoWhile (SBlock []) [LN \"1\"])"
+
+  shouldBe (testStatement "do {break} while (1)")
+    "Right (SDoWhile (SBlock [SBreak Nothing]) [LN \"1\"])"
+
+  shouldBe (testStatement "do x = 1; while (1)")
+    "Right (SDoWhile (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))) [LN \"1\"])"
+
+  shouldBe (testStatement "for (;;) {}")
+    "Right (SFor (ForRegular Nothing Nothing Nothing) (SBlock []))"
+
+  shouldBe (testStatement "for (;;) {break}")
+    "Right (SFor (ForRegular Nothing Nothing Nothing) (SBlock [SBreak Nothing]))"
+
+  shouldBe (testStatement "for (;;) x = 1")
+    "Right (SFor (ForRegular Nothing Nothing Nothing) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
+
+  shouldBe (testStatement "for (x;;) x = 1")
+    "Right (SFor (ForRegular (Just (BindVar (LI \"x\") Nothing)) Nothing Nothing) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
+
+  shouldBe (testStatement "for (x;;x++) x = 1")
+    "Right (SFor (ForRegular (Just (BindVar (LI \"x\") Nothing)) Nothing (Just (UnaryUpdate \"++\" False (LI \"x\")))) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
+
+  shouldBe (testStatement "for (x;;x+=1) x = 1")
+    "Right (SFor (ForRegular (Just (BindVar (LI \"x\") Nothing)) Nothing (Just (Assignment \"+=\" (LI \"x\") (LN \"1\")))) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
+
+  shouldBe (testStatement "for (;x&&y;) {}")
+    "Right (SFor (ForRegular Nothing (Just (Operation \"&&\" (LI \"x\") (LI \"y\"))) Nothing) (SBlock []))"
+
+  shouldBe (testStatement "for (x in a) {}")
+    "Right (SFor (ForIn (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
+
+  shouldBe (testStatement "for (let x in a) {}")
+    "Right (SFor (ForInV \"let\" (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
+
+  shouldBe (testStatement "for (x of a) {}")
+    "Right (SFor (ForOf (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
+
+  shouldBe (testStatement "for (let x of a) {}")
+    "Right (SFor (ForOfV \"let\" (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
diff --git a/t/Test/MemberExpression.hs b/t/Test/MemberExpression.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/MemberExpression.hs
@@ -0,0 +1,41 @@
+module Test.MemberExpression where
+
+import Test.Hspec
+import Test.Helpers
+
+testNewExpression = do
+    shouldBe (testExpression "new A")
+      "Right (New (LI \"A\"))"
+
+    shouldBe (testExpression "new A()")
+      "Right (New (FCall (LI \"A\") []))"
+
+testFunctionCall = do
+    shouldBe (testExpression "a()")
+      "Right (FCall (LI \"a\") [])"
+
+    shouldBe (testExpression "a.b()")
+      "Right (FCall (Dot (LI \"a\") (LI \"b\")) [])"
+
+    shouldBe (testExpression "a().b")
+      "Right (Dot (FCall (LI \"a\") []) (LI \"b\"))"
+
+    shouldBe (testExpression "a(1).b")
+      "Right (Dot (FCall (LI \"a\") [LN \"1\"]) (LI \"b\"))"
+
+testDotAccessor = do
+    shouldBe (testExpression "a.b")
+      "Right (Dot (LI \"a\") (LI \"b\"))"
+
+testArrayAccessor = do
+    shouldBe (testExpression "a[0]")
+      "Right (Acc (LI \"a\") (LN \"0\"))"
+
+    shouldBe (testExpression "a[b.x]")
+      "Right (Acc (LI \"a\") (Dot (LI \"b\") (LI \"x\")))"
+
+    shouldBe (testExpression "a[b.x].c")
+      "Right (Dot (Acc (LI \"a\") (Dot (LI \"b\") (LI \"x\"))) (LI \"c\"))"
+
+    shouldBe (testExpression "a.c[b.x]")
+      "Right (Acc (Dot (LI \"a\") (LI \"c\")) (Dot (LI \"b\") (LI \"x\")))"
diff --git a/t/Test/Switch.hs b/t/Test/Switch.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Switch.hs
@@ -0,0 +1,26 @@
+module Test.Switch where
+
+import Test.Hspec
+import Test.Helpers
+
+testSwitch = do
+  shouldBe (testStatement "var x;")
+    "Right (SVariable \"var\" [BindVar (LI \"x\") Nothing])"
+
+  shouldBe (testStatement "let x;")
+    "Right (SVariable \"let\" [BindVar (LI \"x\") Nothing])"
+
+  shouldBe (testStatement "var x = 1")
+    "Right (SVariable \"var\" [BindVar (LI \"x\") (Just (LN \"1\"))])"
+
+  shouldBe (testStatement "var x = 1, y;")
+    "Right (SVariable \"var\" [BindVar (LI \"x\") (Just (LN \"1\")),BindVar (LI \"y\") Nothing])"
+
+  shouldBe (testStatement "const x = 1;")
+    "Right (SVariable \"const\" [BindVar (LI \"x\") (Just (LN \"1\"))])"
+
+  shouldBe (testStatement "const x = 1, y = 2;")
+    "Right (SVariable \"const\" [BindVar (LI \"x\") (Just (LN \"1\")),BindVar (LI \"y\") (Just (LN \"2\"))])"
+
+  shouldBe (testStatement "const {a} = {a:1};")
+    "Right (SVariable \"const\" [BindPattern (LO [OPI (LI \"a\")]) (Just (LO [OPKV (LI \"a\") (LN \"1\")]))])"
diff --git a/t/Test/Try.hs b/t/Test/Try.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Try.hs
@@ -0,0 +1,17 @@
+module Test.Try where
+
+import Test.Hspec
+import Test.Helpers
+
+testTry = do
+  shouldBe (testStatement "try {} catch {}")
+    "Right (STry (SBlock []) (SCatch Nothing (SBlock [])) Nothing)"
+
+  shouldBe (testStatement "try {} catch {} finally {}")
+    "Right (STry (SBlock []) (SCatch Nothing (SBlock [])) Nothing)"
+
+  shouldBe (testStatement "try {} catch (e) {} finally {}")
+    "Right (STry (SBlock []) (SCatch (Just (LP (LI \"e\"))) (SBlock [])) Nothing)"
+
+  shouldBe (testStatement "return 1")
+    "Right (SReturn (LN \"1\"))"
diff --git a/t/Test/Variables.hs b/t/Test/Variables.hs
new file mode 100644
--- /dev/null
+++ b/t/Test/Variables.hs
@@ -0,0 +1,26 @@
+module Test.Variables where
+
+import Test.Hspec
+import Test.Helpers
+
+testVariables = do
+  shouldBe (testStatement "var x;")
+    "Right (SVariable \"var\" [BindVar (LI \"x\") Nothing])"
+
+  shouldBe (testStatement "let x;")
+    "Right (SVariable \"let\" [BindVar (LI \"x\") Nothing])"
+
+  shouldBe (testStatement "var x = 1")
+    "Right (SVariable \"var\" [BindVar (LI \"x\") (Just (LN \"1\"))])"
+
+  shouldBe (testStatement "var x = 1, y;")
+    "Right (SVariable \"var\" [BindVar (LI \"x\") (Just (LN \"1\")),BindVar (LI \"y\") Nothing])"
+
+  shouldBe (testStatement "const x = 1;")
+    "Right (SVariable \"const\" [BindVar (LI \"x\") (Just (LN \"1\"))])"
+
+  shouldBe (testStatement "const x = 1, y = 2;")
+    "Right (SVariable \"const\" [BindVar (LI \"x\") (Just (LN \"1\")),BindVar (LI \"y\") (Just (LN \"2\"))])"
+
+  shouldBe (testStatement "const {a} = {a:1};")
+    "Right (SVariable \"const\" [BindPattern (LO [OPI (LI \"a\")]) (Just (LO [OPKV (LI \"a\") (LN \"1\")]))])"
diff --git a/test/Spec.hs b/test/Spec.hs
deleted file mode 100644
--- a/test/Spec.hs
+++ /dev/null
@@ -1,584 +0,0 @@
-import Control.Monad (when)
-import System.Exit
-import Test.Hspec
-import Test.Hspec.Runner
-import Text.Parsec (parse)
-import Language.JS.Parser hiding (parse)
-import qualified Language.JS.Parser as JS
-
-testExpression = parse expressions "dummy.js" >>= return . show
-testStatement = parse statements "dummy.js" >>= return . show
-testTopLevelStatement = parse topLevelStatements "dummy.js" >>= return . show
-
-testParseJs = JS.parse "dummy.js" >>= return . show
-
-testExpressions :: Spec
-testExpressions = describe "Parse literals:" $ do
-  it "this" $ do
-    shouldBe (testExpression "this")
-      "Right LThis"
-
-  it "undefined/null" $ do
-    shouldBe (testExpression "null")
-      "Right LNull"
-
-  it "booleans" $ do
-    shouldBe (testExpression "true")
-      "Right (LB True)"
-
-    shouldBe (testExpression "false")
-      "Right (LB False)"
-
-  it "ident" $ do
-    shouldBe (testExpression "test")
-      "Right (LI \"test\")"
-
-  it "numbers" $ do
-    shouldBe (testExpression "1")
-      "Right (LN \"1\")"
-
-    shouldBe (testExpression "1.10")
-      "Right (LN \"1.10\")"
-
-    shouldBe (testExpression "0.10e3")
-      "Right (LN \"0.10e3\")"
-
-    shouldBe (testExpression "0x11")
-      "Right (LN \"0x11\")"
-
-    shouldBe (testExpression "0b11")
-      "Right (LN \"0b11\")"
-
-  it "strings" $ do
-    shouldBe (testExpression "\"test\"")
-      "Right (LS \"test\")"
-    shouldBe (testExpression "\'test\'")
-      "Right (LS \"test\")"
-
-  it "template string" $ do
-    shouldBe (testExpression "`test`")
-      "Right (LTS [TString \"test\"])"
-
-    shouldBe (testExpression "`${\"a\"}`")
-      "Right (LTS [TExpression (LS \"a\")])"
-
-    shouldBe (testExpression "`test ${a + 1} test`")
-      "Right (LTS [TString \"test \",TExpression (Operation \"+\" (LI \"a\") (LN \"1\")),TString \" test\"])"
-
-    shouldBe (testExpression "`${a} test ${b}`")
-      "Right (LTS [TExpression (LI \"a\"),TString \" test \",TExpression (LI \"b\")])"
-
-  it "arrays" $ do
-    shouldBe (testExpression "[]")
-      "Right (LA [])"
-
-    shouldBe (testExpression "[,]")
-      "Right (LA [Elision])"
-
-    shouldBe (testExpression "[1,2]")
-      "Right (LA [LN \"1\",LN \"2\"])"
-
-    shouldBe (testExpression "[a,2]")
-      "Right (LA [LI \"a\",LN \"2\"])"
-
-    shouldBe (testExpression "[a,...b]")
-      "Right (LA [LI \"a\",Spread (LI \"b\")])"
-
-    shouldBe (testExpression "[a,...[1, 2]]")
-      "Right (LA [LI \"a\",Spread (LA [LN \"1\",LN \"2\"])])"
-
-    shouldBe (testExpression "[a,,b]")
-      "Right (LA [LI \"a\",Elision,LI \"b\"])"
-
-  it "objects" $ do
-    shouldBe (testExpression "{}")
-      "Right (LO [])"
-
-    shouldBe (testExpression "{a:1}")
-      "Right (LO [OPKV (LI \"a\") (LN \"1\")])"
-
-    shouldBe (testExpression "{b:2,c: \"d\"}")
-      "Right (LO [OPKV (LI \"b\") (LN \"2\"),OPKV (LI \"c\") (LS \"d\")])"
-
-    shouldBe (testExpression "{b:2, c:function() {}}")
-      "Right (LO [OPKV (LI \"b\") (LN \"2\"),OPKV (LI \"c\") (Function Nothing [] (SBlock []))])"
-
-    shouldBe (testExpression "{e}")
-      "Right (LO [OPI (LI \"e\")])"
-
-    shouldBe (testExpression "{e,f:1}")
-      "Right (LO [OPI (LI \"e\"),OPKV (LI \"f\") (LN \"1\")])"
-
-    shouldBe (testExpression "{e,...f}")
-      "Right (LO [OPI (LI \"e\"),OPI (Spread (LI \"f\"))])"
-
-    shouldBe (testExpression "{e,...{a:1}}")
-      "Right (LO [OPI (LI \"e\"),OPI (Spread (LO [OPKV (LI \"a\") (LN \"1\")]))])"
-
-    shouldBe (testExpression "{e,i:...{a:1}}")
-      "Right (LO [OPI (LI \"e\"),OPKV (LI \"i\") (Spread (LO [OPKV (LI \"a\") (LN \"1\")]))])"
-
-    shouldBe (testExpression "{e(){}}")
-      "Right (LO [OPM (PropertyMethod (LI \"e\") [] (SBlock []))])"
-
-    shouldBe (testExpression "{get e(){}}")
-      "Right (LO [OPM (ClassGetMethod (PropertyMethod (LI \"e\") [] (SBlock [])))])"
-
-    shouldBe (testExpression "{set e(){}}")
-      "Right (LO [OPM (ClassSetMethod (PropertyMethod (LI \"e\") [] (SBlock [])))])"
-
-    shouldBe (testExpression "{async e(){}}")
-      "Right (LO [OPM (Async (PropertyMethod (LI \"e\") [] (SBlock [])))])"
-
-  it "regular expression" $ do
-    shouldBe (testExpression "/test\\/asdf/")
-      "Right (RegExp \"test\\\\/asdf\" \"\")"
-
-    shouldBe (testExpression "/test\\/asdf/gmi")
-      "Right (RegExp \"test\\\\/asdf\" \"gmi\")"
-
-    shouldBe (testExpression "/test\\/asdf/gmi.exec(test)")
-      "Right (FCall (Dot (RegExp \"test\\\\/asdf\" \"gmi\") (LI \"exec\")) [LI \"test\"])"
-
-  it "try dotted" $ do
-    shouldBe (testExpression "a.b")
-      "Right (Dot (LI \"a\") (LI \"b\"))"
-
-  it "array accessor" $ do
-    shouldBe (testExpression "a[0]")
-      "Right (Acc (LI \"a\") (LN \"0\"))"
-
-    shouldBe (testExpression "a[b.x]")
-      "Right (Acc (LI \"a\") (Dot (LI \"b\") (LI \"x\")))"
-
-    shouldBe (testExpression "a[b.x].c")
-      "Right (Dot (Acc (LI \"a\") (Dot (LI \"b\") (LI \"x\"))) (LI \"c\"))"
-
-    shouldBe (testExpression "a.c[b.x]")
-      "Right (Acc (Dot (LI \"a\") (LI \"c\")) (Dot (LI \"b\") (LI \"x\")))"
-
-  it "parens expression" $ do
-    shouldBe (testExpression "(a,b)")
-      "Right (LP (Comma (LI \"a\") (LI \"b\")))"
-
-  it "function expression" $ do
-    shouldBe (testExpression "function a() {}")
-      "Right (Function (Just (LI \"a\")) [] (SBlock []))"
-
-    shouldBe (testExpression "function() {}")
-      "Right (Function Nothing [] (SBlock []))"
-
-    shouldBe (testExpression "function(a, ...b) {}")
-      "Right (Function Nothing [BindVar (LI \"a\") Nothing,BindRest (LI \"b\")] (SBlock []))"
-
-    shouldBe (testExpression "function(a=1) {}")
-      "Right (Function Nothing [BindVar (LI \"a\") (Just (LN \"1\"))] (SBlock []))"
-
-    shouldBe (testExpression "function(a=1) { return 1; }")
-      "Right (Function Nothing [BindVar (LI \"a\") (Just (LN \"1\"))] (SBlock [SReturn (LN \"1\")]))"
-
-    shouldBe (testExpression "function(a=1) {\n   function b() {} }")
-      "Right (Function Nothing [BindVar (LI \"a\") (Just (LN \"1\"))] (SBlock [SF \"b\" [] (SBlock [])]))"
-
-    shouldBe (testExpression "()=>x")
-      "Right (Arrow (Right []) (SExp (LI \"x\")))"
-
-    shouldBe (testExpression "()=>{x}")
-      "Right (Arrow (Right []) (SBlock [SExp (LI \"x\")]))"
-
-    shouldBe (testExpression "x=>{x}")
-      "Right (Arrow (Left (BindVar (LI \"x\") Nothing)) (SBlock [SExp (LI \"x\")]))"
-
-  it "class expression" $ do
-    shouldBe (testExpression "class {}")
-      "Right (Class Nothing Nothing (SBlock []))"
-
-    shouldBe (testExpression "class test {}")
-      "Right (Class (Just (LI \"test\")) Nothing (SBlock []))"
-
-    shouldBe (testExpression "class test extends A {}")
-      "Right (Class (Just (LI \"test\")) (Just (LI \"A\")) (SBlock []))"
-
-    shouldBe (testExpression "class extends A {}")
-      "Right (Class Nothing (Just (LI \"A\")) (SBlock []))"
-
-    shouldBe (testExpression "class extends A { x = 1 }")
-      "Right (Class Nothing (Just (LI \"A\")) (SBlock [SExp (ClassProperty (LI \"x\") (LN \"1\"))]))"
-
-    shouldBe (testExpression "class extends A { x() {} }")
-      "Right (Class Nothing (Just (LI \"A\")) (SBlock [SExp (PropertyMethod (LI \"x\") [] (SBlock []))]))"
-
-    shouldBe (testExpression "class { static x = 1 }")
-      "Right (Class Nothing Nothing (SBlock [SExp (ClassStatic (ClassProperty (LI \"x\") (LN \"1\")))]))"
-
-    shouldBe (testExpression "class { set x() { return 1 } }")
-      "Right (Class Nothing Nothing (SBlock [SExp (ClassSetMethod (PropertyMethod (LI \"x\") [] (SBlock [SReturn (LN \"1\")])))]))"
-
-    shouldBe (testExpression "class { get x() { return 1 } }")
-      "Right (Class Nothing Nothing (SBlock [SExp (ClassGetMethod (PropertyMethod (LI \"x\") [] (SBlock [SReturn (LN \"1\")])))]))"
-
-    shouldBe (testExpression "class { async x() { return 1 } }")
-      "Right (Class Nothing Nothing (SBlock [SExp (Async (PropertyMethod (LI \"x\") [] (SBlock [SReturn (LN \"1\")])))]))"
-
-  it "new expression" $ do
-    shouldBe (testExpression "new A")
-      "Right (New (LI \"A\"))"
-
-    shouldBe (testExpression "new A()")
-      "Right (New (FCall (LI \"A\") []))"
-
-  it "call expression" $ do
-    shouldBe (testExpression "a()")
-      "Right (FCall (LI \"a\") [])"
-
-    shouldBe (testExpression "a.b()")
-      "Right (FCall (Dot (LI \"a\") (LI \"b\")) [])"
-
-    shouldBe (testExpression "a().b")
-      "Right (Dot (FCall (LI \"a\") []) (LI \"b\"))"
-
-    shouldBe (testExpression "a(1).b")
-      "Right (Dot (FCall (LI \"a\") [LN \"1\"]) (LI \"b\"))"
-
-  it "unary expression" $ do
-    shouldBe (testExpression "+a")
-      "Right (Unary \"+\" (LI \"a\"))"
-
-    shouldBe (testExpression "-a")
-      "Right (Unary \"-\" (LI \"a\"))"
-
-    shouldBe (testExpression "~a")
-      "Right (Unary \"~\" (LI \"a\"))"
-
-    shouldBe (testExpression "!a")
-      "Right (Unary \"!\" (LI \"a\"))"
-
-    shouldBe (testExpression "delete a")
-      "Right (Unary \"delete\" (LI \"a\"))"
-
-    shouldBe (testExpression "typeof a")
-      "Right (Unary \"typeof\" (LI \"a\"))"
-
-    shouldBe (testExpression "void a")
-      "Right (Unary \"void\" (LI \"a\"))"
-
-    shouldBe (testExpression "++a")
-      "Right (UnaryUpdate \"++\" True (LI \"a\"))"
-
-    shouldBe (testExpression "a++")
-      "Right (UnaryUpdate \"++\" False (LI \"a\"))"
-
-    shouldBe (testExpression "--a")
-      "Right (UnaryUpdate \"--\" True (LI \"a\"))"
-
-    shouldBe (testExpression "a--")
-      "Right (UnaryUpdate \"--\" False (LI \"a\"))"
-
-  it "spread expression" $ do
-    shouldBe (testExpression "{...a}")
-      "Right (LO [OPI (Spread (LI \"a\"))])"
-
-  it "assignment expression" $ do
-    shouldBe (testExpression "x = 1")
-      "Right (Assignment \"=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x += 1")
-      "Right (Assignment \"+=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x -= 1")
-      "Right (Assignment \"-=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x *= 1")
-      "Right (Assignment \"*=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x /= 1")
-      "Right (Assignment \"/=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x %= 1")
-      "Right (Assignment \"%=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x &= 1")
-      "Right (Assignment \"&=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x |= 1")
-      "Right (Assignment \"|=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x **= 1")
-      "Right (Assignment \"**=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x >>= 1")
-      "Right (Assignment \">>=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x <<= 1")
-      "Right (Assignment \"<<=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x >>>= 1")
-      "Right (Assignment \">>>=\" (LI \"x\") (LN \"1\"))"
-
-    shouldBe (testExpression "x.a += 1")
-      "Right (Assignment \"+=\" (Dot (LI \"x\") (LI \"a\")) (LN \"1\"))"
-
-  it "operation expressions" $ do
-     shouldBe (testExpression "a + b")
-       "Right (Operation \"+\" (LI \"a\") (LI \"b\"))"
-
-     shouldBe (testExpression "a - b")
-       "Right (Operation \"-\" (LI \"a\") (LI \"b\"))"
-
-     shouldBe (testExpression "a * b")
-       "Right (Operation \"*\" (LI \"a\") (LI \"b\"))"
-
-     shouldBe (testExpression "a / b")
-       "Right (Operation \"/\" (LI \"a\") (LI \"b\"))"
-
-     shouldBe (testExpression "a = b - c")
-       "Right (Assignment \"=\" (LI \"a\") (Operation \"-\" (LI \"b\") (LI \"c\")))"
-
-     shouldBe (testExpression "a + b - c")
-       "Right (Operation \"-\" (Operation \"+\" (LI \"a\") (LI \"b\")) (LI \"c\"))"
-
-     shouldBe (testExpression "a + x.a - x.c")
-       "Right (Operation \"-\" (Operation \"+\" (LI \"a\") (Dot (LI \"x\") (LI \"a\"))) (Dot (LI \"x\") (LI \"c\")))"
-
-     shouldBe (testExpression "a + x.a && x.c")
-       "Right (Operation \"&&\" (Operation \"+\" (LI \"a\") (Dot (LI \"x\") (LI \"a\"))) (Dot (LI \"x\") (LI \"c\")))"
-
-     shouldBe (testExpression "a == x.a + x.c")
-       "Right (Operation \"==\" (LI \"a\") (Operation \"+\" (Dot (LI \"x\") (LI \"a\")) (Dot (LI \"x\") (LI \"c\"))))"
-
-     shouldBe (testExpression "!a")
-       "Right (Unary \"!\" (LI \"a\"))"
-
-     shouldBe (testExpression "a = x.a && x.c")
-       "Right (Assignment \"=\" (LI \"a\") (Operation \"&&\" (Dot (LI \"x\") (LI \"a\")) (Dot (LI \"x\") (LI \"c\"))))"
-
-     shouldBe (testExpression "a instanceof c")
-       "Right (Operation \"instanceof\" (LI \"a\") (LI \"c\"))"
-
-     shouldBe (testExpression "typeof a == \"number\"")
-       "Right (Operation \"==\" (Unary \"typeof\" (LI \"a\")) (LS \"number\"))"
-
-     shouldBe (testExpression "x ? y : z")
-       "Right (Condition (LI \"x\") (LI \"y\") (LI \"z\"))"
-
-     shouldBe (testExpression "x == 1 ? y : z")
-       "Right (Condition (Operation \"==\" (LI \"x\") (LN \"1\")) (LI \"y\") (LI \"z\"))"
-
-     shouldBe (testExpression "a = x == 1 ? y + 1 : z && 6")
-       "Right (Assignment \"=\" (LI \"a\") (Condition (Operation \"==\" (LI \"x\") (LN \"1\")) (Operation \"+\" (LI \"y\") (LN \"1\")) (Operation \"&&\" (LI \"z\") (LN \"6\"))))"
-
-     shouldBe (testExpression "a.b() || x")
-       "Right (Operation \"||\" (FCall (Dot (LI \"a\") (LI \"b\")) []) (LI \"x\"))"
-
-     shouldBe (testExpression "(x)\n.y")
-       "Right (Dot (LP (LI \"x\")) (LI \"y\"))"
-
-  it "empty expression" $ do
-    shouldBe (testExpression ";")
-      "Right Empty"
-
-testStatements = describe "Statements" $ do
-  it "empty statement" $ do
-    shouldBe (testStatement ";")
-      "Right (SExp Empty)"
-
-  it "if statement" $ do
-    shouldBe (testStatement "if (1) x = 1")
-      "Right (SIf (LP (LN \"1\")) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))) Nothing)"
-
-    shouldBe (testStatement "if (1)\nx = 1")
-      "Right (SIf (LP (LN \"1\")) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))) Nothing)"
-
-    shouldBe (testStatement "if (1) {x = 1}")
-      "Right (SIf (LP (LN \"1\")) (SBlock [SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))]) Nothing)"
-
-    shouldBe (testStatement "if (1) {x = 1} else x = 2")
-      "Right (SIf (LP (LN \"1\")) (SBlock [SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))]) (Just (SExp (Assignment \"=\" (LI \"x\") (LN \"2\")))))"
-
-    shouldBe (testStatement "if (a in [1, 2]) {}")
-      "Right (SIf (LP (Operation \"in\" (LI \"a\") (LA [LN \"1\",LN \"2\"]))) (SBlock []) Nothing)"
-
-  it "try statement" $ do
-    shouldBe (testStatement "try {} catch {}")
-      "Right (STry (SBlock []) (SCatch Nothing (SBlock [])) Nothing)"
-
-    shouldBe (testStatement "try {} catch {} finally {}")
-      "Right (STry (SBlock []) (SCatch Nothing (SBlock [])) Nothing)"
-
-    shouldBe (testStatement "try {} catch (e) {} finally {}")
-      "Right (STry (SBlock []) (SCatch (Just (LP (LI \"e\"))) (SBlock [])) Nothing)"
-
-    shouldBe (testStatement "return 1")
-      "Right (SReturn (LN \"1\"))"
-
-  it "throw statements" $ do
-    shouldBe (testStatement "throw x;")
-      "Right (SThrow (LI \"x\"))"
-
-  it "variable statements" $ do
-    shouldBe (testStatement "var x;")
-      "Right (SVariable \"var\" [BindVar (LI \"x\") Nothing])"
-
-    shouldBe (testStatement "let x;")
-      "Right (SVariable \"let\" [BindVar (LI \"x\") Nothing])"
-
-    shouldBe (testStatement "var x = 1")
-      "Right (SVariable \"var\" [BindVar (LI \"x\") (Just (LN \"1\"))])"
-
-    shouldBe (testStatement "var x = 1, y;")
-      "Right (SVariable \"var\" [BindVar (LI \"x\") (Just (LN \"1\")),BindVar (LI \"y\") Nothing])"
-
-    shouldBe (testStatement "const x = 1;")
-      "Right (SVariable \"const\" [BindVar (LI \"x\") (Just (LN \"1\"))])"
-
-    shouldBe (testStatement "const x = 1, y = 2;")
-      "Right (SVariable \"const\" [BindVar (LI \"x\") (Just (LN \"1\")),BindVar (LI \"y\") (Just (LN \"2\"))])"
-
-    shouldBe (testStatement "const {a} = {a:1};")
-      "Right (SVariable \"const\" [BindPattern (LO [OPI (LI \"a\")]) (Just (LO [OPKV (LI \"a\") (LN \"1\")]))])"
-
-  it "switch statement" $ do
-    shouldBe (testStatement "switch (a) {}")
-      "Right (SSwitch (LP (LI \"a\")) [])"
-
-    shouldBe (testStatement "switch (a) { case 1: { return; } }")
-      "Right (SSwitch (LP (LI \"a\")) [SCase [Case (LN \"1\")] [SBlock [SReturn Empty]]])"
-
-    shouldBe (testStatement "switch (a) { case 1: { return; } break }")
-      "Right (SSwitch (LP (LI \"a\")) [SCase [Case (LN \"1\")] [SBlock [SReturn Empty],SBreak Nothing]])"
-
-    shouldBe (testStatement "switch (a) { case 1: default: { return; } }")
-      "Right (SSwitch (LP (LI \"a\")) [SCase [Case (LN \"1\"),DefaultCase] [SBlock [SReturn Empty]]])"
-
-    shouldBe (testStatement "switch (a) { case 1: { return 1; } case 2: default: { return; } }")
-      "Right (SSwitch (LP (LI \"a\")) [SCase [Case (LN \"1\")] [SBlock [SReturn (LN \"1\")]],SCase [Case (LN \"2\"),DefaultCase] [SBlock [SReturn Empty]]])"
-
-    shouldBe (testStatement "switch (a) { case 1: { return 1; } break; default: return; }")
-      "Right (SSwitch (LP (LI \"a\")) [SCase [Case (LN \"1\")] [SBlock [SReturn (LN \"1\")],SBreak Nothing],SCase [DefaultCase] [SReturn Empty]])"
-
-  it "continue statement" $ do
-    shouldBe (testStatement "continue")
-      "Right (SContinue Nothing)"
-
-    shouldBe (testStatement "continue x")
-      "Right (SContinue (Just (LI \"x\")))"
-
-  it "debugger statement" $ do
-    shouldBe (testStatement "debugger")
-      "Right SDebugger"
-
-  it "iteration statement" $ do
-    shouldBe (testStatement "while (1) {}")
-      "Right (SWhile [LN \"1\"] (SBlock []))"
-
-    -- break is allowed here
-    shouldBe (testStatement "while (1) {break}")
-      "Right (SWhile [LN \"1\"] (SBlock [SBreak Nothing]))"
-
-    shouldBe (testStatement "while (1) {\nbreak\n;}")
-      "Right (SWhile [LN \"1\"] (SBlock [SBreak Nothing]))"
-
-    shouldBe (testStatement "while (1) x = 1")
-      "Right (SWhile [LN \"1\"] (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
-
-    shouldBe (testStatement "do {} while (1)")
-      "Right (SDoWhile (SBlock []) [LN \"1\"])"
-
-    shouldBe (testStatement "do {break} while (1)")
-      "Right (SDoWhile (SBlock [SBreak Nothing]) [LN \"1\"])"
-
-    shouldBe (testStatement "do x = 1; while (1)")
-      "Right (SDoWhile (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))) [LN \"1\"])"
-
-    shouldBe (testStatement "for (;;) {}")
-      "Right (SFor (ForRegular Nothing Nothing Nothing) (SBlock []))"
-
-    shouldBe (testStatement "for (;;) {break}")
-      "Right (SFor (ForRegular Nothing Nothing Nothing) (SBlock [SBreak Nothing]))"
-
-    shouldBe (testStatement "for (;;) x = 1")
-      "Right (SFor (ForRegular Nothing Nothing Nothing) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
-
-    shouldBe (testStatement "for (x;;) x = 1")
-      "Right (SFor (ForRegular (Just (BindVar (LI \"x\") Nothing)) Nothing Nothing) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
-
-    shouldBe (testStatement "for (x;;x++) x = 1")
-      "Right (SFor (ForRegular (Just (BindVar (LI \"x\") Nothing)) Nothing (Just (UnaryUpdate \"++\" False (LI \"x\")))) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
-
-    shouldBe (testStatement "for (x;;x+=1) x = 1")
-      "Right (SFor (ForRegular (Just (BindVar (LI \"x\") Nothing)) Nothing (Just (Assignment \"+=\" (LI \"x\") (LN \"1\")))) (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
-
-    shouldBe (testStatement "for (;x&&y;) {}")
-      "Right (SFor (ForRegular Nothing (Just (Operation \"&&\" (LI \"x\") (LI \"y\"))) Nothing) (SBlock []))"
-
-    shouldBe (testStatement "for (x in a) {}")
-      "Right (SFor (ForIn (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
-
-    shouldBe (testStatement "for (let x in a) {}")
-      "Right (SFor (ForInV \"let\" (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
-
-    shouldBe (testStatement "for (x of a) {}")
-      "Right (SFor (ForOf (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
-
-    shouldBe (testStatement "for (let x of a) {}")
-      "Right (SFor (ForOfV \"let\" (BindVar (LI \"x\") Nothing) (LI \"a\")) (SBlock []))"
-
-  it "with statement" $ do
-    shouldBe (testStatement "with (a) {}")
-      "Right (SWith (LI \"a\") (SBlock []))"
-
-  it "labelled statement" $ do
-    shouldBe (testStatement "a: x = 1")
-      "Right (SLabel (LI \"a\") (SExp (Assignment \"=\" (LI \"x\") (LN \"1\"))))"
-
-  it "import statement" $ do
-    shouldBe (testTopLevelStatement "import \"test.js\"")
-      "Right (SImportFile (LS \"test.js\"))"
-
-    shouldBe (testTopLevelStatement "import * as A from \"test.js\"")
-      "Right (SImport (Left (Namespace (LI \"A\"))) (LS \"test.js\"))"
-
-    shouldBe (testTopLevelStatement "import {x} from \"test.js\"")
-      "Right (SImport (Right [BindNames [LI \"x\"]]) (LS \"test.js\"))"
-
-    shouldBe (testTopLevelStatement "import B, {x} from \"test.js\"")
-      "Right (SImport (Right [DefaultName (LI \"B\"),BindNames [LI \"x\"]]) (LS \"test.js\"))"
-
-  it "export statement" $ do
-    shouldBe (testTopLevelStatement "export {}")
-      "Right (SExport (SBlock []))"
-
-    shouldBe (testTopLevelStatement "export default {}")
-      "Right (SExportDefault (LO []))"
-
-    shouldBe (testTopLevelStatement "export {} from \"test.js\"")
-      "Right (SRExport (LO []) (LS \"test.js\"))"
-
-    shouldBe (testTopLevelStatement "export const a = 1;")
-      "Right (SExport (SVariable \"const\" [BindVar (LI \"a\") (Just (LN \"1\"))]))"
-
-  it "function declaration" $ do
-    shouldBe (testTopLevelStatement "function a() {} function b() {}")
-      "Right (SF \"a\" [] (SBlock []))"
-
-    shouldBe (testTopLevelStatement "function a() { var i; return 1; }")
-      "Right (SF \"a\" [] (SBlock [SVariable \"var\" [BindVar (LI \"i\") Nothing],SReturn (LN \"1\")]))"
-
-    shouldBe (testTopLevelStatement "function a() { function b() { return () => 1; } return 1; }")
-      "Right (SF \"a\" [] (SBlock [SF \"b\" [] (SBlock [SReturn (Arrow (Right []) (SExp (LN \"1\")))]),SReturn (LN \"1\")]))"
-
-  it "class declaration" $ do
-    shouldBe (testTopLevelStatement "class a {}")
-      "Right (SC \"a\" Nothing (SBlock []))"
-
-testAll :: Spec
-testAll = do
-  testExpressions
-  testStatements
-
-main :: IO ()
-main = do
-  summary <- hspecWithResult defaultConfig testAll
-  when (summaryFailures summary == 0)
-    exitSuccess
-  exitFailure
