content: {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  import Text.Parsec
  import Text.Parsec.Text
  --import Text.Parsec.Expr
  import qualified Text.Parsec.Token as Tok
  import qualified Text.Parsec.Language as Lang
  import qualified Data.Text as T
  
  import Data.Functor.Identity (Identity)
  
  
  lett :: T.Text
  lett = "abcdefghijklmnopqrstuvwxyz"
  
  num :: T.Text
  num = "1234567890"
  
  lexer :: Tok.GenTokenParser T.Text () Identity
  lexer = Tok.makeTokenParser style 
  
  style :: Tok.GenLanguageDef T.Text () Identity
  style = Lang.emptyDef { 
    Tok.commentStart = "{-"
    , Tok.commentEnd = "-}"
    , Tok.identStart = lett <|> oneOf "+-/*"
    , Tok.identLetter = lett <|> num <|> oneOf "_'"
    , Tok.reservedOpNames = [ "'", "\""]
    , Tok.reservedNames = [ "true", "false"
      , "let", "quote", "lambda"
      , "Nil" ]
    }
  
  
  {-
  Tok.TokenParser { parens = m_parens
             , identifier = m_identifier -- Tok.Identifer lexer
             , reservedOp = m_reservedOp
             , reserved = m_reserved
             , semiSep1 = m_semiSep1
             , whiteSpace = m_whiteSpace } = makeTokenParser style
  -}
  
  reservedOp :: T.Text -> Parser ()
  reservedOp op = Tok.reservedOp lexer (T.unpack op) 
  
  parseAtom :: Parser LispVal
  parseAtom = Atom . T.pack <$> Tok.identifier lexer
  
  parseString :: Parser LispVal 
  parseString = 
    do reservedOp "\""
       p <- Tok.identifier lexer
       reservedOp "\"" 
       return $ Str (T.pack p) 
  
  parseNumber :: Parser LispVal 
  parseNumber = fmap (Num . read) $ many1 digit
          
  parseList :: Parser LispVal
  parseList = List . concat <$>  (many parseExpr `sepBy` char ' ')
  
  {-
  parseSExp1 :: Parser LispVal
  parseSExp1 = List . concat <$>  Tok.parens (many parseExpr `sepBy` char ' ')
  -}
  
  parseSExp :: Parser LispVal
  parseSExp = 
    do reservedOp "("
       p <- (many parseExpr `sepBy` char ' ')
       reservedOp ")"
       return $ List . concat $ p 
  
  parseQuote :: Parser LispVal
  parseQuote = 
    do 
      reservedOp "\'" 
      x <- parseExpr
      return $ List [Atom "quote", x] 
  
  parseExpr :: Parser LispVal 
  parseExpr = parseAtom
        <|> parseString
        <|> parseNumber
        <|> parseReserved
        <|> parseQuote
        <|> parseSExp
  
  
  parseReserved = 
    do 
      reservedOp "Nil" >> return Nil
      <|> (reservedOp "#t" >> return (Bin True))
      <|> (reservedOp "#f" >> return (Bin False))
  
  
  
  contents :: Parser a -> Parser a
  contents p = do
    Tok.whiteSpace lexer
    r <- p
    eof 
    return r
  
  
  
  readExpr :: T.Text -> Either ParseError LispVal
  readExpr = parse (contents parseExpr) "<stdin>" 
  
  
  -------------------------
  --  STAND ALONE TEST
  --  --------------------
  
  p pa inp = case parse pa "" inp of
               { Left err -> "err " ++ show err
               ; Right ans -> "ans " ++ show ans
               }
  
  
  -- need a copy of LispVal for stand alone
  data LispVal = Nil | Bin Bool | Atom T.Text | Num Int | Str T.Text | List [LispVal] deriving (Show)
  main :: IO ()
  main = 
    do 
      print $ p parseReserved "Nil"
      print $ p parseExpr  "Nil"
      print $ p parseExpr  "'Nil"
      print " "
      print $ p parseQuote  "'(1 2 3 4)"
      print $ p parseQuote  "'x"
      print $ p parseQuote  "'()"
      print " "
      print " "
      print $ p (parseExpr) "(1)"
      print $ p parseList  "a \"a\" \"a\""
      print $ p parseList  "x 1 2"
      print $ p parseSExp  "(a \"a\" \"a\")"
      print $ p parseSExp  "(1 2 3 4)"
      print " "
      print " "
      --print $ p (m_parens (many parseExpr `sepBy` char ' ')) "(lambda (fnName a b c) (body) )"
      print $ p parseSExp  "(lambda (fnName a b c) (body) )"
      print $ p parseSExp  "(a 1 b 2)"
      print $ p parseSExp  "(let (a 1 b 2) (fn a b) )"
      print $ p parseSExp  "(let (a (x 1 2) b (y 3 4)) (fn a b) )"
      print " "
      print " "
      print $ p parseExpr "x"
      print $ p parseExpr "1"
      print $ p parseExpr "\"a b c d\""
      print $ p parseExpr "(3 1)"
      print " "
      print $ p parseReserved  "#t"
      print $ p parseReserved  "#f"
      print $ p parseExpr "#t"
      print $ p parseExpr "#f"
      print " "
      print $ p parseExpr "(eq? 1 2)"
      print $ p parseExpr "1"
  