content: 
  module Attoparsec (runSexp, runLongws, runNumcsv, runTm) where
  
  import Control.Applicative
  import Data.Attoparsec.ByteString.Char8
  import Common
  import qualified Data.ByteString as B
  
  isLatinLetter :: Char -> Bool
  isLatinLetter c = ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
  
  skipWhile1 :: (Char -> Bool) -> Parser ()
  skipWhile1 pred = satisfy pred *> skipWhile pred
  
  ws, open, close, ident, sexp :: Parser ()
  ws      = skipWhile (\c -> c == ' ' || c == '\n')
  open    = char '(' >> ws
  close   = char ')' >> ws
  ident   = skipWhile1 isLatinLetter <* ws
  sexp    = (open *> skipMany1 sexp <* close) <|> ident
  runSexp = parseOnly sexp
  
  longw, longws :: Parser ()
  longw     = () <$ string "thisisalongkeyword"
  longws    = skipMany1 (longw *> ws) <* endOfInput
  runLongws = parseOnly longws
  
  numeral, comma, numcsv :: Parser ()
  numeral   = skipWhile1 (\c -> '0' <= c && c <= '9') >> ws
  comma     = char ',' >> ws
  numcsv    = numeral >> skipMany1 (comma >> numeral) >> endOfInput
  runNumcsv = parseOnly numcsv
  
  
  --------------------------------------------------------------------------------
  
  ident' :: Parser B.ByteString
  ident' = takeWhile1 (\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 #-}
  
  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 {_ <- string "fun"; ws; x <- ident'; dot; t <- tm; pure (Lam x t)})
    <|> (do {_ <- string "let"; ws; x <- ident'; equal; t <- tm; semi; u <- tm; pure (Let x t u)})
    <|> add
  
  runTm = parseOnly (ws *> tm <* endOfInput)
  