content: 
  module Parsec (runSexp, runLongws, runNumcsv, runTm) where
  
  import Control.Monad
  import Text.Parsec hiding (chainl, digit)
  import Text.Parsec.ByteString
  import qualified Data.ByteString as B
  import Data.Char hiding (isDigit)
  import Common
  
  isLatinLetter :: Char -> Bool
  isLatinLetter c = ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
  
  isDigit :: Char -> Bool
  isDigit c = '0' <= c && c <= '9'
  
  ws, open, close, ident, sexp :: Parser ()
  ws      = skipMany (satisfy \c -> c == ' ' || c == '\n')
  open    = char '(' >> ws
  close   = char ')' >> ws
  ident   = skipMany1 (satisfy isLatinLetter) <* ws
  sexp    = (open *> skipMany1 sexp <* close) <|> ident
  runSexp = parse sexp ""
  
  byteString :: B.ByteString -> Parser ()
  byteString b = do
    i <- getInput
    guard $ B.isPrefixOf b i
    setInput $ B.drop (B.length b) i
  
  longw, longws :: Parser ()
  longw     = () <$ byteString "thisisalongkeyword"
  longws    = skipMany1 (longw *> ws) <* eof
  runLongws = parse longws ""
  
  numeral, comma, numcsv :: Parser ()
  numeral   = skipMany1 (satisfy \c -> '0' <= c && c <= '9') >> ws
  comma     = char ',' >> ws
  numcsv    = numeral >> skipMany1 (comma >> numeral) >> eof
  runNumcsv = parse numcsv ""
  
  --------------------------------------------------------------------------------
  
  {-# inline byteStringOf #-}
  byteStringOf :: Parser a -> Parser B.ByteString
  byteStringOf p = do
    i <- getInput
    _ <- p
    i' <- getInput
    pure $! B.take (B.length i - B.length i') i
  
  ident' :: Parser B.ByteString
  ident' = byteStringOf (skipMany1 (satisfy \c -> isLatinLetter c || isDigit c)) <* ws
  
  equal = char '=' >> ws
  semi  = char ';' >> ws
  dot   = char '.' >> ws
  addOp = char '+' >> ws
  mulOp = char '*' >> ws
  parl  = char '(' >> ws
  parr  = char ')' >> ws
  
  chainl :: (b -> a -> b) -> Parser b -> Parser a -> Parser b
  chainl f start elem = start >>= go where
    go b = do {!a <- elem; go $! f b a} <|> pure b
  {-# inline chainl #-}
  
  digit :: Parser Int
  digit = digitToInt <$> satisfy isDigit
  
  decimal :: Parser Int
  decimal = chainl (\x n -> 10*x + n) digit digit
  
  add :: Parser Tm
  add = chainl Add mul (addOp *> mul)
  
  mul :: Parser Tm
  mul = chainl Mul spine (mulOp *> spine)
  
  spine :: Parser Tm
  spine = chainl App atom atom
  
  atom :: Parser Tm
  atom =
          (Int <$> (decimal <* ws))
      <|> (Var <$> ident')
      <|> (parl *> tm <* parr)
  
  tm :: Parser Tm
  tm =  (do {byteString "fun"; ws; x <- ident'; dot; t <- tm; pure (Lam x t)})
    <|> (do {byteString "let"; ws; x <- ident'; equal; t <- tm; semi; u <- tm; pure (Let x t u)})
    <|> add
  
  runTm = parse (ws *> tm <* eof) ""
  