module TestImport
  ( testParseExpr
  , testPrintExpr
  , Name(..)
  , ValueExpr(..)
  -- * Re-exports
  , module X
  )
where

-- re-exports
import           Language.Egison.Syntax.Pattern.Expr
                                               as X

-- main
import           Data.Text                      ( Text
                                                , pack
                                                )
import           Data.Functor                   ( void )
import           Data.Void                      ( Void )
import           Control.Applicative            ( (<|>)
                                                , some
                                                )
import           Control.Monad.Except           ( MonadError )

import           Text.Megaparsec                ( Parsec )
import qualified Text.Megaparsec               as Parsec
                                                ( chunk
                                                , single
                                                , parse
                                                )
import qualified Text.Megaparsec.Char          as Parsec
                                                ( letterChar )
import qualified Text.Megaparsec.Char.Lexer    as Parsec
                                                ( decimal )

import           Language.Egison.Syntax.Pattern.Expr
                                                ( Expr(..) )
import           Language.Egison.Parser.Pattern ( ParseMode(..)
                                                , ParseFixity(..)
                                                , Fixity(..)
                                                , Precedence(..)
                                                , Associativity(..)
                                                , Errors
                                                , parseExpr
                                                )
import           Language.Egison.Pretty.Pattern ( PrintMode(..)
                                                , PrintFixity(..)
                                                , Error
                                                , prettyExpr
                                                )


newtype Name = Name String
  deriving (Show, Eq, Ord)

newtype ValueExpr = ValueExprInt Int
  deriving newtype Num
  deriving stock (Show, Eq)

unParsec :: Parsec Void String a -> (String -> Either String a)
unParsec p input = case Parsec.parse p "test" input of
  Left  e -> Left (show e)
  Right x -> Right x

testFixities :: [Fixity Name]
testFixities =
  [ Fixity AssocRight (Precedence 5) (Name "++")
  , Fixity AssocRight (Precedence 5) (Name ":")
  , Fixity AssocLeft  (Precedence 4) (Name "|>")
  , Fixity AssocRight (Precedence 4) (Name "<|")
  ]

toParseFixity :: Fixity Name -> ParseFixity Name String
toParseFixity fixity@(Fixity _ _ (Name name)) = ParseFixity fixity
  $ unParsec parser
  where parser = void $ Parsec.chunk name

toPrintFixity :: Fixity Name -> PrintFixity Name
toPrintFixity fixity@(Fixity _ _ (Name name)) = PrintFixity fixity $ pack name

testParseName :: Parsec Void String Name
testParseName = withParens <|> name
 where
  name       = Name <$> some Parsec.letterChar
  ops        = Parsec.chunk "++"
  withParens = do
    op <- Parsec.single '(' *> ops <* Parsec.single ')'
    pure $ Name op

testParseValueExpr :: Parsec Void String ValueExpr
testParseValueExpr = withParens <|> dec
 where
  dec        = ValueExprInt <$> Parsec.decimal
  withParens = do
    d <- Parsec.chunk "(-" *> dec <* Parsec.single ')'
    pure $ negate d

testParseMode :: ParseMode Name Name ValueExpr String
testParseMode = ParseMode { fixities        = map toParseFixity testFixities
                          , blockComment    = Just ("{-", "-}")
                          , lineComment     = Just "--"
                          , varNameParser   = unParsec testParseName
                          , nameParser      = unParsec testParseName
                          , valueExprParser = unParsec testParseValueExpr
                          }

testPrintMode :: PrintMode Name Name ValueExpr
testPrintMode = PrintMode { fixities         = map toPrintFixity testFixities
                          , varNamePrinter   = namePrinter
                          , namePrinter
                          , valueExprPrinter = valueExprPrinter
                          , pageMode         = Nothing
                          }
 where
  namePrinter (Name name) = pack name
  valueExprPrinter (ValueExprInt i) = pack $ show i

testParseExpr
  :: MonadError (Errors String) m => String -> m (Expr Name Name ValueExpr)
testParseExpr = parseExpr testParseMode "test"

testPrintExpr :: MonadError (Error Name) m => Expr Name Name ValueExpr -> m Text
testPrintExpr = prettyExpr testPrintMode