module Text.Haiji.Syntax.Identifier
       ( Identifier
       , identifier
       ) where

import Control.Applicative
import Control.Monad
import Data.Attoparsec.Text
import Data.String

newtype Identifier = Identifier { unIdentifier :: String } deriving Eq

instance Show Identifier where
  show = unIdentifier

instance IsString Identifier where
  fromString = Identifier

-- | python identifier
--
-- https://docs.python.org/2.7/reference/lexical_analysis.html#identifiers
--
-- >>> import Control.Arrow (left)
-- >>> let eval = left (const "parse error") . parseOnly identifier
-- >>> eval "a"
-- Right a
-- >>> eval "ab"
-- Right ab
-- >>> eval "A"
-- Right A
-- >>> eval "Ab"
-- Right Ab
-- >>> eval "_"
-- Right _
-- >>> eval "_a"
-- Right _a
-- >>> eval "_1"
-- Right _1
-- >>> eval "__"
-- Right __
-- >>> eval "_ "
-- Right _
-- >>> eval " _"
-- Left "parse error"
-- >>> eval "and"
-- Left "parse error"
-- >>> eval "1"
-- Left "parse error"
-- >>> eval "1b"
-- Left "parse error"
-- >>> eval "'x"
-- Left "parse error"
--
identifier :: Parser Identifier
identifier = do
  h <- letter <|> char '_'
  ts <- many (letter <|> digit <|> char '_')
  let candidate = h : ts
  when (candidate `elem` keywords) $ fail "identifier"
  return $ Identifier candidate

-- | python keywords
--
-- https://docs.python.org/2.7/reference/lexical_analysis.html#keywords
--
keywords :: [String]
keywords = words "and       del       from      not       while \
                 \as        elif      global    or        with  \
                 \assert    else      if        pass      yield \
                 \break     except    import    print           \
                 \class     exec      in        raise           \
                 \continue  finally   is        return          \
                 \def       for       lambda    try             "