diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -7,7 +7,9 @@
 into S-expressions. Just write a grammar once and get both parser and
 pretty-printer, for free.
 
-The package is heavily inspired by the paper
+**WARNING: highly unstable and experimental software. Not intended for production**
+
+The approach used in `sexp-grammar` is inspired by the paper
 [Invertible syntax descriptions: Unifying parsing and pretty printing]
 (http://www.informatik.uni-marburg.de/~rendel/unparse/) and a similar
 implementation of invertible grammar approach for JSON, library by Martijn van
@@ -26,7 +28,7 @@
   } deriving (Show, Generic)
 
 personGrammar :: SexpG Person
-personGrammar = with $                -- Person is isomorphic to
+personGrammar = with $                -- Person is isomorphic to:
   list (                              -- a list with
     el (sym "person") >>>             -- a symbol "person",
     el string'        >>>             -- a string, and
@@ -35,8 +37,8 @@
       Kw "age"     .:? int))          -- an optional keyword :age with int value.
 ```
 
-So now we can use `personGrammar` to parse S-expressions to `Person`
-record and pretty-print records of `Person` type back to S-expression:
+So now we can use `personGrammar` to parse S-expressions to records of type
+`Person` and pretty-print records of type `Person` back to S-expressions:
 
 ```haskell
 ghci> import Language.SexpGrammar
@@ -44,7 +46,7 @@
 ghci> person <- either error id . decodeWith personGrammar . B8.pack <$> getLine
 (person "John Doe" :address "42 Whatever str." :age 25)
 ghci> person
-Right (Person {pName = "John Doe", pAddress = "42 Whatever str.", pAge = Just 25})
+Person {pName = "John Doe", pAddress = "42 Whatever str.", pAge = Just 25}
 ghci> either print B8.putStrLn . encodeWith personGrammar $ person
 (person "John Doe" :address "42 Whatever str." :age 25)
 ```
diff --git a/bench/Main.hs b/bench/Main.hs
--- a/bench/Main.hs
+++ b/bench/Main.hs
@@ -9,8 +9,8 @@
 
 import Control.Arrow
 import Control.Category
-import qualified Data.ByteString.Lazy.Char8 as B8
 import Data.Data (Data, Typeable)
+import qualified Data.Text.Lazy as TL
 import qualified Language.Sexp as Sexp
 import Language.SexpGrammar
 import Language.SexpGrammar.TH
@@ -69,11 +69,11 @@
 genExpr :: Expr -> Either String Sexp
 genExpr = genSexp sexpIso
 
-expr :: B8.ByteString -> Expr
+expr :: TL.Text -> Expr
 expr = either error id . decode
 
-benchCases :: [(String, B8.ByteString)]
-benchCases = map (\a -> ("expression " ++ take 40 (B8.unpack a) ++ "...", a))
+benchCases :: [(String, TL.Text)]
+benchCases = map (\a -> ("expression " ++ take 40 (TL.unpack a) ++ "...", a))
   [ "(+ 1 20)"
   , "(+ (+ 2 20) 0)"
   , "(+ (+ 3 20) (+ 10 20))"
diff --git a/sexp-grammar.cabal b/sexp-grammar.cabal
--- a/sexp-grammar.cabal
+++ b/sexp-grammar.cabal
@@ -1,5 +1,5 @@
 name:                sexp-grammar
-version:             1.2.1
+version:             1.2.2
 license:             BSD3
 license-file:        LICENSE
 author:              Eugene Smolanka, Sergey Vinokurov
@@ -40,6 +40,7 @@
     Data.InvertibleGrammar.Generic
     Data.InvertibleGrammar.TH
     Control.Monad.ContextError
+    Language.Sexp.LexerInterface
     Language.Sexp.Lexer
     Language.Sexp.Parser
     Language.Sexp.Token
@@ -47,7 +48,6 @@
     Language.SexpGrammar.Base
     Language.SexpGrammar.Class
     Language.SexpGrammar.Combinators
-    Language.SexpGrammar.Parser
 
   build-depends:
       array
@@ -72,13 +72,13 @@
   build-depends:
       QuickCheck
     , base
-    , bytestring
     , scientific
     , semigroups
     , sexp-grammar
     , tasty
     , tasty-hunit
     , tasty-quickcheck
+    , text
   main-is:           Main.hs
   hs-source-dirs:    test
   default-language:  Haskell2010
@@ -92,6 +92,7 @@
     , scientific
     , semigroups
     , sexp-grammar
+    , text
   main-is:           Main.hs
   hs-source-dirs:    bench
   default-language:  Haskell2010
diff --git a/src/Language/Sexp.hs b/src/Language/Sexp.hs
--- a/src/Language/Sexp.hs
+++ b/src/Language/Sexp.hs
@@ -20,7 +20,7 @@
   , getPos
   ) where
 
-import qualified Data.ByteString.Lazy.Char8 as B8
+import qualified Data.Text.Lazy as TL
 
 import Language.Sexp.Types
 import Language.Sexp.Parser (parseSexp_, parseSexps_)
@@ -29,25 +29,25 @@
 import Language.Sexp.Encode (encode)
 
 -- | Quickly decode a ByteString-formatted S-expression into Sexp structure
-decode :: B8.ByteString -> Either String Sexp
+decode :: TL.Text -> Either String Sexp
 decode = parseSexp "<str>"
 
 -- | Parse a ByteString-formatted S-expression into Sexp
 -- structure. Takes file name for better error messages.
-parseSexp :: FilePath -> B8.ByteString -> Either String Sexp
+parseSexp :: FilePath -> TL.Text -> Either String Sexp
 parseSexp fn inp = parseSexp_ (lexSexp (Position fn 1 0) inp)
 
 -- | Parse a ByteString-formatted sequence of S-expressions into list
 -- of Sexp structures. Takes file name for better error messages.
-parseSexps :: FilePath -> B8.ByteString -> Either String [Sexp]
+parseSexps :: FilePath -> TL.Text -> Either String [Sexp]
 parseSexps fn inp = parseSexps_ (lexSexp (Position fn 1 0) inp)
 
 -- | Parse a ByteString-formatted S-expression into Sexp
 -- structure. Takes file name for better error messages.
-parseSexp' :: Position -> B8.ByteString -> Either String Sexp
+parseSexp' :: Position -> TL.Text -> Either String Sexp
 parseSexp' pos inp = parseSexp_ (lexSexp pos inp)
 
 -- | Parse a ByteString-formatted sequence of S-expressions into list
 -- of Sexp structures. Takes file name for better error messages.
-parseSexps' :: Position -> B8.ByteString -> Either String [Sexp]
+parseSexps' :: Position -> TL.Text -> Either String [Sexp]
 parseSexps' pos inp = parseSexps_ (lexSexp pos inp)
diff --git a/src/Language/Sexp/Lexer.x b/src/Language/Sexp/Lexer.x
--- a/src/Language/Sexp/Lexer.x
+++ b/src/Language/Sexp/Lexer.x
@@ -1,5 +1,6 @@
 {
-{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BangPatterns   #-}
+{-# LANGUAGE NamedFieldPuns #-}
 {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
 {-# OPTIONS_GHC -fno-warn-name-shadowing     #-}
 {-# OPTIONS_GHC -fno-warn-tabs               #-}
@@ -16,19 +17,26 @@
 import qualified Data.Text.Lazy as TL
 import Data.Text.Lazy.Encoding (decodeUtf8)
 import qualified Data.ByteString.Lazy.Char8 as B8
+
+import Language.Sexp.LexerInterface
 import Language.Sexp.Token
 import Language.Sexp.Types (Position (..))
-}
 
-%wrapper "posn-bytestring"
+}
 
 $whitechar   = [\ \t\n\r\f\v]
+$unispace    = \x01
+$whitespace  = [$whitechar $unispace]
 
+$uninonspace = \x02
+$uniany      = [$unispace $uninonspace]
+@any         = (. | $uniany)
+
 $digit       = 0-9
 $hex         = [0-9 A-F a-f]
 $alpha       = [a-z A-Z]
 
-$graphic     = [$alpha $digit \!\#\$\%\&\*\+\.\/\<\=\>\?\@\\\^\|\-\~ \(\)\,\;\[\]\`\{\} \:\"\'\_]
+$graphic     = [$alpha $digit \!\#\$\%\&\*\+\.\/\<\=\>\?\@\\\^\|\-\~ \(\)\,\;\[\]\`\{\} \:\"\'\_ $uninonspace]
 
 @intnum      = [\-\+]? $digit+
 @scinum      = [\-\+]? $digit+ ([\.]$digit+)? ([eE] [\-\+]? $digit+)?
@@ -37,15 +45,15 @@
 @escape      = \\ ($charesc | $digit+ | x $hex+)
 @string      = $graphic # [\"\\] | " " | @escape
 
-$idinitial   = [$alpha \!\$\%\&\*\/\<\=\>\?\~\_\^\.\+\-]
-$idsubseq    = [$idinitial $digit \:]
+$idinitial   = [$alpha \!\$\%\&\*\/\<\=\>\?\~\_\^\.\+\- $uninonspace]
+$idsubseq    = [$idinitial $digit \: $uninonspace]
 @identifier  = $idinitial $idsubseq*
 @keyword     = ":" $idsubseq+
 
 :-
 
-$whitechar+        ;
-";".*              ;
+$whitespace+       ;
+";" @any*          ;
 "("                { just TokLParen       }
 ")"                { just TokRParen       }
 "["                { just TokLBracket     }
@@ -63,6 +71,8 @@
 
 {
 
+type AlexAction = LineCol -> TL.Text -> LocatedBy LineCol Token
+
 readInteger :: T.Text -> Integer
 readInteger str =
   case signed decimal str of
@@ -75,18 +85,45 @@
 readString =
   T.pack . read . T.unpack
 
-just :: Token -> AlexPosn -> B8.ByteString -> LocatedBy AlexPosn Token
+just :: Token -> AlexAction
 just tok pos _ =
   L pos tok
 
-via :: (a -> Token) -> (T.Text -> a) -> AlexPosn -> B8.ByteString -> LocatedBy AlexPosn Token
+via :: (a -> Token) -> (T.Text -> a) -> AlexAction
 via ftok f pos str =
-  L pos . ftok . f  . TL.toStrict . decodeUtf8 $ str
+  L pos . ftok . f . TL.toStrict $str
 
-lexSexp :: Position -> B8.ByteString -> [LocatedBy Position Token]
-lexSexp (Position fn line1 col1) = map (mapPosition fixPos) . alexScanTokens
+alexScanTokens :: AlexInput -> [LocatedBy LineCol Token]
+alexScanTokens input =
+  case alexScan input defaultCode of
+    AlexEOF -> []
+    AlexError (AlexInput {aiInput, aiLineCol = LineCol line col}) ->
+      error $ "Lexical error at line " ++ show line ++ " column " ++ show col ++
+        ". Remaining input: " ++ TL.unpack (TL.take 1000 aiInput)
+    AlexSkip input _ -> alexScanTokens input
+    AlexToken input' tokLen action ->
+      action (aiLineCol input) inputText : alexScanTokens input'
+      where
+        -- It is safe to take token length from input because every byte Alex
+        -- sees corresponds to exactly one character, even if character is a
+        -- Unicode one that occupies several bytes. We do character translation
+        -- in LexerInterface.alexGetByte function so that all unicode characters
+        -- occupy single byte.
+        --
+        -- On the other hand, taking N characters from Text will take N valid
+        -- characters, not N bytes.
+        --
+        -- Thus, we're good.
+        inputText = TL.take (fromIntegral tokLen) $ aiInput input
   where
-    fixPos (AlexPn _ l c) | l == 1    = Position fn line1 (col1 + c)
-                          | otherwise = Position fn (pred l + line1) c
+    defaultCode :: Int
+    defaultCode = 0
+
+lexSexp :: Position -> TL.Text -> [LocatedBy Position Token]
+lexSexp (Position fn line1 col1) =
+  map (mapPosition fixPos) . alexScanTokens . mkAlexInput
+  where
+    fixPos (LineCol l c) | l == 1    = Position fn line1 (col1 + c)
+                         | otherwise = Position fn (pred l + line1) c
 
 }
diff --git a/src/Language/Sexp/LexerInterface.hs b/src/Language/Sexp/LexerInterface.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Sexp/LexerInterface.hs
@@ -0,0 +1,86 @@
+{-# LANGUAGE BangPatterns   #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Language.Sexp.LexerInterface
+  ( LineCol(..)
+  , AlexInput(..)
+  , mkAlexInput
+  -- Alex interfare
+  , alexInputPrevChar
+  , alexGetByte
+  ) where
+
+import Control.Applicative ((<|>))
+import Data.Char
+import Data.Maybe
+import qualified Data.Text.Lazy as TL
+import Data.Word (Word8)
+
+data LineCol = LineCol {-# UNPACK #-} !Int {-# UNPACK #-} !Int
+
+columnsInTab :: Int
+columnsInTab = 8
+
+advanceLineCol :: Char -> LineCol -> LineCol
+advanceLineCol '\n' (LineCol line _)   = LineCol (line + 1) 0
+advanceLineCol '\t' (LineCol line col) = LineCol line (col + columnsInTab)
+advanceLineCol _    (LineCol line col) = LineCol line (col + 1)
+
+data AlexInput = AlexInput
+  { aiInput    :: TL.Text
+  , aiPrevChar :: {-# UNPACK #-} !Char
+  , aiLineCol  :: !LineCol
+  }
+
+mkAlexInput :: TL.Text -> AlexInput
+mkAlexInput source = AlexInput
+  { aiInput    = stripBOM source
+  , aiPrevChar = '\n'
+  , aiLineCol  = initPos
+  }
+  where
+    initPos :: LineCol
+    initPos = LineCol 1 0
+    stripBOM :: TL.Text -> TL.Text
+    stripBOM xs =
+      fromMaybe xs $
+      TL.stripPrefix utf8BOM xs <|> TL.stripPrefix utf8BOM' xs
+    utf8BOM  = "\xFFEF"
+    utf8BOM' = "\xFEFF"
+
+-- Alex interface - functions usedby Alex
+alexInputPrevChar :: AlexInput -> Char
+alexInputPrevChar = aiPrevChar
+
+alexGetByte :: AlexInput -> Maybe (Word8, AlexInput)
+alexGetByte input@AlexInput {aiInput, aiLineCol} =
+  case TL.uncons aiInput of
+    Nothing      -> Nothing
+    Just (c, cs) -> Just $ encode c cs
+  where
+    encode :: Char -> TL.Text -> (Word8, AlexInput)
+    encode c cs = (b, input')
+      where
+        b :: Word8
+        b = fromIntegral $ ord $ fixChar c
+        input' :: AlexInput
+        input' = input
+          { aiInput    = cs
+          , aiPrevChar = c
+          , aiLineCol  = advanceLineCol c aiLineCol
+          }
+
+-- Translate unicode character into special symbol we taught Alex to recognize.
+fixChar :: Char -> Char
+fixChar c
+  -- Plain ascii case
+  | c <= '\x7f' = c
+  -- Unicode caset
+  | otherwise
+  = case generalCategory c of
+        Space -> space
+        _     -> nonSpaceUnicode
+  where
+    space           = '\x01'
+    nonSpaceUnicode = '\x02'
diff --git a/src/Language/Sexp/Pretty.hs b/src/Language/Sexp/Pretty.hs
--- a/src/Language/Sexp/Pretty.hs
+++ b/src/Language/Sexp/Pretty.hs
@@ -52,13 +52,13 @@
   pretty = ppSexp
 
 -- | Pretty-print a Sexp to a Text
-prettySexp' :: Sexp -> Lazy.Text
-prettySexp' = displayT . renderPretty 0.75 79 . ppSexp
+prettySexp :: Sexp -> Lazy.Text
+prettySexp = displayT . renderPretty 0.75 79 . ppSexp
 
 -- | Pretty-print a Sexp to a ByteString
-prettySexp :: Sexp -> ByteString
-prettySexp = encodeUtf8 . prettySexp'
+prettySexp' :: Sexp -> ByteString
+prettySexp' = encodeUtf8 . prettySexp
 
 -- | Pretty-print a list of Sexps as a sequence of S-expressions to a ByteString
-prettySexps :: [Sexp] -> ByteString
-prettySexps = encodeUtf8 . displayT . renderPretty 0.75 79 . vcat . punctuate (line <> line) . map ppSexp
+prettySexps :: [Sexp] -> Lazy.Text
+prettySexps = displayT . renderPretty 0.75 79 . vcat . punctuate (line <> line) . map ppSexp
diff --git a/src/Language/Sexp/Types.hs b/src/Language/Sexp/Types.hs
--- a/src/Language/Sexp/Types.hs
+++ b/src/Language/Sexp/Types.hs
@@ -1,6 +1,3 @@
-{-# LANGUAGE DeriveFoldable    #-}
-{-# LANGUAGE DeriveFunctor     #-}
-{-# LANGUAGE DeriveTraversable #-}
 {-# LANGUAGE FlexibleInstances #-}
 
 module Language.Sexp.Types
diff --git a/src/Language/SexpGrammar.hs b/src/Language/SexpGrammar.hs
--- a/src/Language/SexpGrammar.hs
+++ b/src/Language/SexpGrammar.hs
@@ -102,6 +102,7 @@
   ) where
 
 import Data.ByteString.Lazy.Char8 (ByteString)
+import qualified Data.Text.Lazy as TL
 import Data.InvertibleGrammar
 import Data.InvertibleGrammar.Monad
 
@@ -132,12 +133,12 @@
 
 -- | Deserialize a value from a lazy 'ByteString'. The input must
 -- contain exactly one S-expression. Comments are ignored.
-decode :: SexpIso a => ByteString -> Either String a
+decode :: SexpIso a => TL.Text -> Either String a
 decode =
   decodeWith sexpIso
 
 -- | Like 'decode' but uses specified grammar.
-decodeWith :: SexpG a -> ByteString -> Either String a
+decodeWith :: SexpG a -> TL.Text -> Either String a
 decodeWith g input =
   Sexp.decode input >>= parseSexp g
 
@@ -159,21 +160,21 @@
 -- one S-expression. Unlike 'decode' it takes an additional argument
 -- with a file name which is being parsed. It is used for error
 -- messages.
-decodeNamed :: SexpIso a => FilePath -> ByteString -> Either String a
+decodeNamed :: SexpIso a => FilePath -> TL.Text -> Either String a
 decodeNamed fn =
   decodeNamedWith sexpIso fn
 
 -- | Like 'decodeNamed' but uses specified grammar.
-decodeNamedWith :: SexpG a -> FilePath -> ByteString -> Either String a
+decodeNamedWith :: SexpG a -> FilePath -> TL.Text -> Either String a
 decodeNamedWith g fn input =
   Sexp.parseSexp fn input >>= parseSexp g
 
 -- | Pretty-prints a value serialized to a lazy 'ByteString'.
-encodePretty :: SexpIso a => a -> Either String ByteString
+encodePretty :: SexpIso a => a -> Either String TL.Text
 encodePretty =
   encodePrettyWith sexpIso
 
 -- | Like 'encodePretty' but uses specified grammar.
-encodePrettyWith :: SexpG a -> a -> Either String ByteString
+encodePrettyWith :: SexpG a -> a -> Either String TL.Text
 encodePrettyWith g =
   fmap Sexp.prettySexp . genSexp g
diff --git a/src/Language/SexpGrammar/Base.hs b/src/Language/SexpGrammar/Base.hs
--- a/src/Language/SexpGrammar/Base.hs
+++ b/src/Language/SexpGrammar/Base.hs
@@ -36,7 +36,7 @@
 
 import Data.InvertibleGrammar
 import Data.InvertibleGrammar.Monad
-import Language.Sexp.Pretty (prettySexp')
+import Language.Sexp.Pretty (prettySexp)
 import Language.Sexp.Types
 
 -- | Grammar which matches Sexp to a value of type a and vice versa.
@@ -51,11 +51,11 @@
 
 unexpectedSexp :: (MonadContextError (Propagation Position) (GrammarError Position) m) => Text -> Sexp -> m a
 unexpectedSexp exp got =
-  grammarError $ expected exp `mappend` unexpected (Lazy.toStrict $ prettySexp' got)
+  grammarError $ expected exp `mappend` unexpected (Lazy.toStrict $ prettySexp got)
 
 unexpectedAtom :: (MonadContextError (Propagation Position) (GrammarError Position) m) => Atom -> Atom -> m a
 unexpectedAtom expected atom = do
-  unexpectedSexp (Lazy.toStrict $ prettySexp' (Atom dummyPos expected)) (Atom dummyPos atom)
+  unexpectedSexp (Lazy.toStrict $ prettySexp (Atom dummyPos expected)) (Atom dummyPos atom)
 
 unexpectedAtomType :: (MonadContextError (Propagation Position) (GrammarError Position) m) => Text-> Atom -> m a
 unexpectedAtomType expected atom = do
@@ -66,6 +66,7 @@
 -- Top-level grammar
 
 data SexpGrammar a b where
+  GPos  :: SexpGrammar (Sexp :- t) (Position :- Sexp :- t)
   GAtom :: Grammar AtomGrammar (Atom :- t) t' -> SexpGrammar (Sexp :- t) t'
   GList :: Grammar SeqGrammar t            t' -> SexpGrammar (Sexp :- t) t'
   GVect :: Grammar SeqGrammar t            t' -> SexpGrammar (Sexp :- t) t'
@@ -74,6 +75,9 @@
   ( MonadPlus m
   , MonadContextError (Propagation Position) (GrammarError Position) m
   ) => InvertibleGrammar m SexpGrammar where
+  forward GPos (s :- t) =
+    return (getPos s :- s :- t)
+
   forward (GAtom g) (s :- t) =
     case s of
       Atom p a    -> dive $ locate p >> forward g (a :- t)
@@ -89,6 +93,9 @@
       Vector p xs -> dive $ locate p >> parseSequence xs g t
       other       -> locate (getPos other) >> unexpectedSexp "vector" other
 
+  backward GPos (_ :- s :- t) =
+    return (s :- t)
+
   backward (GAtom g) t = do
     (a :- t') <- dive $ backward g t
     return (Atom dummyPos a :- t')
@@ -177,7 +184,7 @@
   (a, SeqCtx rest) <- runStateT (forward g t) (SeqCtx xs)
   unless (null rest) $
     unexpectedStr $ "leftover elements: " `mappend`
-      (Lazy.toStrict $ Lazy.unwords $ map prettySexp' rest)
+      (Lazy.toStrict $ Lazy.unwords $ map prettySexp rest)
   return a
 
 data SeqGrammar a b where
@@ -226,14 +233,14 @@
     when (not $ M.null ctx) $
       unexpectedStr $ "property-list keys: " `mappend`
         (Lazy.toStrict $ Lazy.unwords $
-          map (prettySexp' . Atom dummyPos . AtomKeyword) (M.keys ctx))
+          map (prettySexp . Atom dummyPos . AtomKeyword) (M.keys ctx))
     return res
     where
       go [] props = return props
       go (Atom _ (AtomKeyword kwd):x:xs) props = step >> go xs (M.insert kwd x props)
       go other _ =
         unexpectedStr $ "malformed property-list: " `mappend`
-          (Lazy.toStrict $ Lazy.unwords $ map prettySexp' other)
+          (Lazy.toStrict $ Lazy.unwords $ map prettySexp other)
 
   backward (GElem g) t = do
     step
@@ -284,7 +291,7 @@
     case M.lookup kwd ps of
       Nothing -> unexpectedStr $
         mconcat [ "key "
-                , Lazy.toStrict . prettySexp' . Atom dummyPos . AtomKeyword $ kwd
+                , Lazy.toStrict . prettySexp . Atom dummyPos . AtomKeyword $ kwd
                 , " not found"
                 ]
       Just x  -> do
diff --git a/src/Language/SexpGrammar/Combinators.hs b/src/Language/SexpGrammar/Combinators.hs
--- a/src/Language/SexpGrammar/Combinators.hs
+++ b/src/Language/SexpGrammar/Combinators.hs
@@ -30,6 +30,7 @@
   , (.:)
   , (.:?)
   -- ** Utility grammars
+  , position
   , pair
   , unpair
   , swap
@@ -146,6 +147,11 @@
 -- | Define a grammar for a constant keyword
 kw :: Kw -> SexpG_
 kw = Inject . GAtom . Inject . GKw
+
+-- | Get position of Sexp. Doesn't consume Sexp and doesn't have any
+-- effect on backward run.
+position :: Grammar SexpGrammar (Sexp :- t) (Position :- Sexp :- t)
+position = Inject GPos
 
 ----------------------------------------------------------------------
 -- Special combinators
diff --git a/src/Language/SexpGrammar/Parser.hs b/src/Language/SexpGrammar/Parser.hs
deleted file mode 100644
--- a/src/Language/SexpGrammar/Parser.hs
+++ /dev/null
@@ -1,53 +0,0 @@
-{-# LANGUAGE CPP                   #-}
-{-# LANGUAGE DeriveFunctor         #-}
-{-# LANGUAGE FlexibleInstances     #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-
-module Language.SexpGrammar.Parser where
-
-import Control.Applicative
-#if MIN_VERSION_mtl(2, 2, 0)
-import Control.Monad.Except
-#else
-import Control.Monad.Error
-#endif
-
-data Result a
-  = Success a
-  | Failure String
-  deriving (Functor)
-
-instance Applicative Result where
-  pure = Success
-  Success f <*> Success a = Success (f a)
-  Failure a <*> Success _ = Failure a
-  Success _ <*> Failure b = Failure b
-  Failure a <*> Failure b = Failure $ a ++ "\n" ++ b
-
-instance Monad Result where
-  return = Success
-  Failure a >>= _ = Failure a
-  Success a >>= f = f a
-
-instance Alternative Result where
-  empty = Failure "empty"
-  Success a <|> _ = Success a
-  Failure _ <|> Success b = Success b
-  Failure a <|> Failure b = Failure (a ++ "\n" ++ b)
-
-instance MonadPlus Result where
-  mzero = empty
-  mplus = (<|>)
-
-instance MonadError [Char] Result where
-  throwError = Failure
-  catchError res handle =
-    case res of
-      Success a -> Success a
-      Failure b -> handle b
-
-runR :: (a -> Result b) -> a -> Either String b
-runR parser a =
-  case parser a of
-    Success a -> Right a
-    Failure b -> Left $ "List of failures:\n" ++ b
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -19,7 +19,7 @@
 #endif
 
 import Control.Category
-import qualified Data.ByteString.Lazy.Char8 as B8
+import qualified Data.Text.Lazy as TL
 import Data.Scientific
 import Data.Semigroup
 import Test.QuickCheck ()
@@ -48,7 +48,7 @@
 stripPos (Quoted _ x)  = Quoted dummyPos $ stripPos x
 
 parseSexp' :: String -> Either String Sexp
-parseSexp' input = stripPos <$> Sexp.decode (B8.pack input)
+parseSexp' input = stripPos <$> Sexp.decode (TL.pack input)
 
 data Pair a b = Pair a b
   deriving (Show, Eq, Ord, Generic)
@@ -145,6 +145,23 @@
     parseSexp' "-123" @?= Right (Int' (- 123))
   , testCase "+123.4e5 is a floating number" $
     parseSexp' "+123.4e5" @?= Right (Real' (read "+123.4e5" :: Scientific))
+  , testCase "comments" $
+    parseSexp' ";; hello, world\n   123" @?= Right (Int' 123)
+  , testCase "cyrillic characters in comments" $
+    parseSexp' ";; привет!\n   123" @?= Right (Int' 123)
+  , testCase "unicode math in comments" $
+    parseSexp' ";; Γ ctx\n;; ----- Nat-formation\n;; Γ ⊦ Nat : Type\nfoobar" @?=
+      Right (Symbol' "foobar")
+  , testCase "symbol" $
+    parseSexp' "hello-world" @?= Right (Symbol' "hello-world")
+  , testCase "cyrillic symbol" $
+    parseSexp' "привет-мир" @?= Right (Symbol' "привет-мир")
+  , testCase "string with arabic characters" $
+    parseSexp' "\"ي الخاطفة الجديدة، مع, بلديهم\"" @?=
+    Right (String' "ي الخاطفة الجديدة، مع, بلديهم")
+  , testCase "string with japanese characters" $
+    parseSexp' "\"媯綩 づ竤バ り姥娩ぎょひ\"" @?=
+    Right (String' "媯綩 づ竤バ り姥娩ぎょひ")
   ]
 
 grammarTests :: TestTree
