Problem01A.hs

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DeriveFunctor  #-}
{-# LANGUAGE LambdaCase     #-}
{-# LANGUAGE MultiWayIf     #-}

module Problem01A where

import           Control.Applicative
import           Control.Monad
import           Data.Bifunctor
import           Data.Foldable       (foldl')
import           Data.List.NonEmpty  (NonEmpty (..))
import           Data.Semigroup


-- Header

newtype Parser t a = Parser
  { unParser :: ParseState t -> ParseResult (a, ParseState t)
  } deriving Functor

data ParseState t = ParseState Position [t]
  deriving (Show)

initialParseState :: [t] -> ParseState t
initialParseState xs = ParseState 0 xs

nextParseState :: ParseState t -> Maybe (t, ParseState t)
nextParseState (ParseState pos0 s0) = case s0 of
  []   -> Nothing
  t:s1 -> Just (t, ParseState (pos0 + 1) s1)

type Position = Int

data ParseResult a
  = ParseSuccess (NonEmpty a)
  | ParseFailure [(Position, String)]
  deriving (Show, Functor)

fromParseResult :: ParseResult a -> Either [(Position, String)] a
fromParseResult = \case
  ParseSuccess (x :| _) -> Right x
  ParseFailure es       -> Left $ take 10 es

instance Applicative ParseResult where
  pure x = ParseSuccess (x :| [])
  (<*>) = ap

instance Monad ParseResult where
  ParseSuccess xs >>= f = sconcat $ f <$> xs
  ParseFailure es >>= _ = ParseFailure es

parseFail :: ParseState t -> String -> ParseResult a
parseFail (ParseState pos _) e = ParseFailure [(pos, e)]

instance Semigroup (ParseResult a) where
  ParseSuccess xs1 <> ParseSuccess xs2 = ParseSuccess $ xs1 <> xs2
  ParseFailure _   <> ParseSuccess xs2 = ParseSuccess xs2
  ParseSuccess xs1 <> ParseFailure _   = ParseSuccess xs1
  ParseFailure es1 <> ParseFailure es2 = ParseFailure $ es1 <> es2

runParser :: Parser t a -> [t] -> Either [(Position, String)] a
runParser (Parser p) s = second fst $ fromParseResult $ p $ initialParseState s

instance Applicative (Parser t) where
  pure x = Parser \s -> pure (x, s)

  Parser pf <*> Parser px = Parser \s0 -> do
    (f, s1) <- pf s0
    (x, s2) <- px s1
    pure (f x, s2)

instance Monad (Parser t) where
  Parser p >>= f = Parser \s0 -> do
    (r, s1) <- p s0
    let Parser pr = f r
    pr s1

instance MonadFail (Parser t) where
  fail e = Parser \s -> parseFail s e

instance Alternative (Parser t) where
  empty = fail "empty"

  Parser p1 <|> Parser p2 = Parser \s0 -> p1 s0 <> p2 s0

eofP :: Parser t ()
eofP = Parser \s0 -> case nextParseState s0 of
  Nothing -> pure ((), s0)
  _       -> parseFail s0 "not end yet"

takeTokenP :: Parser t t
takeTokenP = Parser \s0 -> case nextParseState s0 of
  Just (t, s1) -> pure (t, s1)
  Nothing      -> parseFail s0 "no tokens"

takeMapP :: (t -> Either String a) -> Parser t a
takeMapP f = Parser \s0 -> case nextParseState s0 of
  Just (t, s1) -> case f t of
    Right x -> pure (x, s1)
    Left  e -> parseFail s0 e
  Nothing      -> parseFail s0 "no tokens"

tokenP :: Eq t => t -> Parser t t
tokenP x = takeMapP \case
  t | t == x -> Right x
  _          -> Left "no matched token"

manyP :: Parser t a -> Parser t [a]
manyP p = goP
  where
    goP = ((:) <$> p <*> goP) <|> pure []


-- Main

type Lit = Int
type Var = String

data Token
  = TInt Int
  | TIdent String
  | TSymbol String
  | TArrow
  | TBackSlash
  | TParensL
  | TParensR
  deriving (Eq, Show)

prettyTokens :: [Token] -> String
prettyTokens = go
  where
    go []     = ""
    go [t]    = pToken t
    go (t:ts) = pToken t <> " " <> go ts

    pToken = \case
      TInt    x  -> show x
      TIdent  x  -> x
      TSymbol x  -> x
      TArrow     -> "->"
      TBackSlash -> "\\"
      TParensL   -> "("
      TParensR   -> ")"

data Expr
  = App Expr Expr
  | InfixApp Expr Var Expr
  | Abs Var Expr
  | Var Var
  | Lit Lit
  deriving (Eq, Show)


type TokenParser = Parser Token

programP :: TokenParser Expr
programP = exprP <* eofP

exprP :: TokenParser Expr
exprP = blockExprP

blockExprP :: TokenParser Expr
blockExprP
    =   absP
    <|> infixExpr1P
  where
    absP = Abs <$> (tokenP TBackSlash *> varP) <*> (tokenP TArrow *> exprP)

infixExpr1P :: TokenParser Expr
infixExpr1P
  =   (InfixApp <$> infixExpr2P <*> varopTokenP "+" <*> infixExpr1P)
  <|> (InfixApp <$> infixExpr2P <*> varopTokenP "-" <*> infixExpr1P)
  <|> infixExpr2P

infixExpr2P :: TokenParser Expr
infixExpr2P
  =   (InfixApp <$> infixExpr3P <*> varopTokenP "*" <*> infixExpr2P)
  <|> (InfixApp <$> infixExpr3P <*> varopTokenP "/" <*> infixExpr2P)
  <|> infixExpr3P

infixExpr3P :: TokenParser Expr
infixExpr3P = fexprP

fexprP :: TokenParser Expr
fexprP = foldl' App <$> aexprP <*> manyP aexprP

aexprP :: TokenParser Expr
aexprP
  =   (tokenP TParensL *> exprP <* tokenP TParensR)
  <|> (Lit <$> litP)
  <|> (Var <$> varP)

litP :: TokenParser Lit
litP = takeMapP \case
  TInt x -> Right x
  _      -> Left "no literal token"

varP :: TokenParser Var
varP
  =   identP
  <|> (tokenP TParensL *> symbolP <* tokenP TParensR)

varopTokenP :: String -> TokenParser Var
varopTokenP x = (tokenP $ TSymbol x) *> pure x

identP :: TokenParser Var
identP = takeMapP \case
  TIdent x -> Right x
  _        -> Left "no ident token"

symbolP :: TokenParser Var
symbolP = takeMapP \case
  TSymbol x -> Right x
  _         -> Left "no ident token"


-- Sample

-- |
--
-- @@
-- \x -> x
-- @@
--
-- >>> runParser exprP sampleToken1
-- Right (Abs "x" (Var "x"))
--
sampleToken1 :: [Token]
sampleToken1 =
  [ TBackSlash
  , TIdent "x"
  , TArrow
  , TIdent "x"
  ]

-- |
--
-- @@
-- \f -> \x -> ((x + f 1 * 2)) / 3
-- @@
--
-- >>> runParser exprP sampleToken2
-- Right (Abs "f" (Abs "x" (InfixApp (InfixApp (Var "x") "+" (InfixApp (App (Var "f") (Lit 1)) "*" (Lit 2))) "/" (Lit 3))))
--
sampleToken2 :: [Token]
sampleToken2 =
  [ TBackSlash
  , TIdent "f"
  , TArrow
  , TBackSlash
  , TIdent "x"
  , TArrow
  , TParensL
  , TParensL
  , TIdent "x"
  , TSymbol "+"
  , TIdent "f"
  , TInt 1
  , TSymbol "*"
  , TInt 2
  , TParensR
  , TParensR
  , TSymbol "/"
  , TInt 3
  ]

-- |
--
-- @@
-- \f -> \x -> x + f \y -> x * y
-- @@
--
-- >>> runParser exprP sampleToken3
-- Left ...
--
sampleToken3 :: [Token]
sampleToken3 =
  [ TBackSlash
  , TIdent "f"
  , TArrow
  , TBackSlash
  , TIdent "x"
  , TArrow
  , TIdent "x"
  , TSymbol "+"
  , TIdent "f"
  , TBackSlash
  , TIdent "y"
  , TArrow
  , TIdent "x"
  , TSymbol "*"
  , TIdent "y"
  ]

-- |
--
-- @@
-- \f -> \x -> (((x + f 1) * 2)
-- @@
--
-- >>> runParser exprP sampleToken4
-- Left ...
--
sampleToken4 :: [Token]
sampleToken4 =
  [ TBackSlash
  , TIdent "f"
  , TArrow
  , TBackSlash
  , TIdent "x"
  , TArrow
  , TParensL
  , TParensL
  , TParensL
  , TIdent "x"
  , TSymbol "+"
  , TIdent "f"
  , TInt 1
  , TParensR
  , TSymbol "*"
  , TInt 2
  , TParensR
  ]