module Lang.LamIf.Parser where

import FP
import Lang.LamIf.Syntax

data Keyword = KLambda | KIfZero | KThen | KElse | KLet | KIn
makePrisms ''Keyword

data KeywordPunctuation = KPDot | KPPlus | KPMinus | KPDefEqual
makePrisms ''KeywordPunctuation

data Punctuation = PLParen | PRParen
makePrisms ''Punctuation
    
data Token =
    TKeyword Keyword
  | TKeywordPunctuation KeywordPunctuation
  | TPunctuation Punctuation
  | TInteger โ„ค
  | TSymbol ๐•Š
  | TWhitespace ๐•Š
makePrisms ''Token

data SourceExp = SourceExp
  { sourceExpContext โˆท SourceContext Token
  , sourceExpRawExp โˆท PreExp ๐•Š SourceExp 
  }

stripSourceExp โˆท SourceExp โ†’ Fixed (PreExp ๐•Š)
stripSourceExp (SourceExp _ e) = Fixed $ map stripSourceExp e

instance Pretty SourceExp where
  pretty e = ppVertical
    [ ppHeader "Source:"
    , pretty $ sourceExpContext e
    , ppHeader "AST:"
    , pretty $ stripSourceExp e
    ]

tokKeyword โˆท Parser โ„‚ Keyword
tokKeyword = mconcat $ map (\ (s,k) โ†’ pWord s โ‰ซ return k)
  [ ("lam",KLambda)
  , ("if0",KIfZero)
  , ("then",KThen)
  , ("else",KElse)
  , ("let",KLet)
  , ("in",KIn)
  ]

tokKeywordPunctuation โˆท Parser โ„‚ KeywordPunctuation
tokKeywordPunctuation = mconcat $ map (\ (s,kp) โ†’ pWord s โ‰ซ return kp)
  [ (".",KPDot)
  , ("+",KPPlus)
  , ("-",KPMinus)
  , (":=",KPDefEqual)
  ]

tokPunctuation โˆท Parser โ„‚ Punctuation
tokPunctuation = mconcat $ map (\ (s,p) โ†’ pWord s โ‰ซ return p)
  [ ("(",PLParen)
  , (")",PRParen)
  ]

tokToken โˆท Parser โ„‚ Token
tokToken = mconcat
  [ construct tKeywordL            ^$ pRender UL              $ pRender (FG darkYellow) tokKeyword
  , construct tKeywordPunctuationL ^$ pRender (FG darkYellow) $ tokKeywordPunctuation
  , construct tPunctuationL        ^$ pRender (FG darkGray)   $ tokPunctuation
  , construct tIntegerL            ^$ pRender (FG darkRed)    $ pInteger
  , construct tSymbolL โˆ˜ ๐•ค         ^$ id                      $ pOneOrMoreGreedy pLetter
  , construct tWhitespaceL         ^$ id                      $ pWhitespaceGreedy
  ]

parWhitespace โˆท Parser Token ()
parWhitespace = void $ pShaped "whitespace" $ view tWhitespaceL

parOptionalWhitespace โˆท Parser Token ()
parOptionalWhitespace = void $ pOptionalGreedy parWhitespace

parSurroundOptionalWhitespace โˆท Parser Token a โ†’ Parser Token a
parSurroundOptionalWhitespace = pSurrounded parOptionalWhitespace

parSymbol โˆท Parser Token ๐•Š
parSymbol = pShaped "symbol" $ view tSymbolL

parLParen โˆท Parser Token ()
parLParen = pShaped "lparen" $ view $ pLParenL โŒพ tPunctuationL

parRParen โˆท Parser Token ()
parRParen = pShaped "rparen" $ view $ pRParenL โŒพ tPunctuationL

parParens โˆท Parser Token a โ†’ Parser Token a
parParens = pSurroundedBy parLParen parRParen โˆ˜ parSurroundOptionalWhitespace

foldSourceExp โˆท FullContextAnnotated Token (PreExp ๐•Š SourceExp) โ†’ SourceExp
foldSourceExp (FullContextAnnotated pc e) = SourceExp pc e

unfoldSourceExp โˆท SourceExp โ†’ FullContextAnnotated Token (PreExp ๐•Š SourceExp)
unfoldSourceExp (SourceExp pc e) = FullContextAnnotated pc e

parMixes โˆท MixfixF Token (FullContextAnnotated Token) (PreExp ๐•Š SourceExp)
parMixes = concat
  [ mixF $ TerminalF $ (fullContextAnnotatedValue โˆ˜ unfoldSourceExp) ^$ parParens parExp
  , mixF $ TerminalF $ EAtom โˆ˜ AInteger ^$ pShaped "integer" $ view tIntegerL
  , mixF $ TerminalF $ EAtom โˆ˜ AVar ^$ parSymbol
  , mixF $ PreF (๐•Ÿ 0) $ pAppendError "lambda prefix" $ do
      void $ pShaped "lambda" $ view $ kLambdaL โŒพ tKeywordL
      x โ† parSurroundOptionalWhitespace $ pRender (FG darkTeal) parSymbol
      void $ pShaped "dot" $ view $ kPDotL โŒพ tKeywordPunctuationL
      parOptionalWhitespace
      return $ \ (foldSourceExp โ†’ e) โ†’ EAtom $ ALam x e
  , mixF $ PreF (๐•Ÿ 0) $ pAppendError "let prefix" $ do
      void $ pShaped "let" $ view $ kLetL โŒพ tKeywordL
      x โ† parSurroundOptionalWhitespace $ pRender (FG darkTeal) parSymbol
      void $ pShaped ":=" $ view $ kPDefEqualL โŒพ tKeywordPunctuationL
      eโ‚ โ† parSurroundOptionalWhitespace parExp
      void $ pShaped "in" $ view $ kInL โŒพ tKeywordL
      parOptionalWhitespace
      return $ \ (foldSourceExp โ†’ eโ‚‚) โ†’ ELet x eโ‚ eโ‚‚
  , mixF $ PreF (๐•Ÿ 0) $ pAppendError "if prefix" $ do
      void $ pShaped "if0" $ view $ kIfZeroL โŒพ tKeywordL
      eโ‚ โ† parSurroundOptionalWhitespace parExp
      void $ pShaped "then" $ view $ kThenL โŒพ tKeywordL
      eโ‚‚ โ† parSurroundOptionalWhitespace parExp
      void $ pShaped "else" $ view $ kElseL โŒพ tKeywordL
      parOptionalWhitespace
      return $ \ (foldSourceExp โ†’ eโ‚ƒ) โ†’ EIf eโ‚ eโ‚‚ eโ‚ƒ
  , mixF $ InfrF (๐•Ÿ 5) $ pAppendError "plus" $ do
      parSurroundOptionalWhitespace $ pShaped "+" $ view $ kPPlusL โŒพ tKeywordPunctuationL
      return $ \ (foldSourceExp โ†’ eโ‚) (foldSourceExp โ†’ eโ‚‚) โ†’ EOp Plus eโ‚ eโ‚‚
  , mixF $ InfF (๐•Ÿ 5) $ pAppendError "minus" $ do
      parSurroundOptionalWhitespace $ pShaped "-" $ view $ kPMinusL โŒพ tKeywordPunctuationL
      return $ \ (foldSourceExp โ†’ eโ‚) (foldSourceExp โ†’ eโ‚‚) โ†’ EOp Minus eโ‚ eโ‚‚
  , mixF $ InflF (๐•Ÿ 100) $ pAppendError "application" $ do
      parWhitespace
      return $ \ (foldSourceExp โ†’ eโ‚) (foldSourceExp โ†’ eโ‚‚) โ†’ EApp eโ‚ eโ‚‚
  ]

parExp โˆท Parser Token SourceExp
parExp = foldSourceExp ^$ pError "exp" $ mixfixParserF parMixes $ \ eM โ†’ do
  (e,pc) โ† pCaptureFull eM
  return $ FullContextAnnotated pc e

parseExp โˆท ๐•Š โ†’ Doc โจ„ SourceExp
parseExp cs = parse parExp *$ tokenize tokToken $ tokens cs

-- - Old, before I figured out how to plumb comonadic structure for context
--
-- parMixes โˆท Mixfix Token ParsedExp
-- parMixes = concat
--   [ mix $ Terminal $ parCaptureExp $ unfoldAnnotatedExp ^$ parParens parExp
--   , mix $ Terminal $ parCaptureExp $ EAtom โˆ˜ AInteger ^$ pShaped "integer" $ view tIntegerL
--   , mix $ Terminal $ parCaptureExp $ EAtom โˆ˜ AVar ^$ parSymbol
--   , mix $ Pre (๐•Ÿ 0) $ do
--       (pc,x) โ† pAppendError "lambda prefix" $ pCaptureFull $ do
--         void $ pShaped "lambda" $ view $ kLambdaL โŒพ tKeywordL
--         x โ† parSurroundOptionalWhitespace $ pRender (FG darkTeal) parSymbol
--         void $ parDot
--         parOptionalWhitespace
--         return x
--       return $ \ (AnnotatedExp pc' e) โ†’ 
--         AnnotatedExp (pc โงบ pc') $ EAtom $ ALam x $ AnnotatedExp pc' e
--   , mix $ Pre (๐•Ÿ 0) $ pAppendError "if prefix" $ do
--       (pc,(eโ‚,eโ‚‚)) โ† pCaptureFull $ do
--         void $ pShaped "if0" $ view $ kIfZeroL โŒพ tKeywordL
--         eโ‚ โ† parSurroundOptionalWhitespace parExp
--         void $ pShaped "then" $ view $ kThenL โŒพ tKeywordL
--         eโ‚‚ โ† parSurroundOptionalWhitespace parExp
--         void $ pShaped "else" $ view $ kElseL โŒพ tKeywordL
--         parOptionalWhitespace
--         return (eโ‚,eโ‚‚)
--       return $ \ (AnnotatedExp pc' eโ‚ƒ) โ†’
--         AnnotatedExp (pc โงบ pc') $ EIf eโ‚ eโ‚‚ $ AnnotatedExp pc' eโ‚ƒ
--   , mix $ Pre (๐•Ÿ 0) $ do
--       (pc,(x,eโ‚)) โ† pAppendError "let prefix" $ pCaptureFull $ do
--         void $ pShaped "let" $ view $ kLetL โŒพ tKeywordL
--         x โ† parSurroundOptionalWhitespace $ pRender (FG darkTeal) parSymbol
--         void $ pShaped ":=" $ view $ kPDefEqualL โŒพ tKeywordPunctuationL
--         eโ‚ โ† parSurroundOptionalWhitespace parExp
--         void $ pShaped "in" $ view $ kInL โŒพ tKeywordL
--         parOptionalWhitespace
--         return (x,eโ‚)
--       return $ \ (AnnotatedExp pc' eโ‚‚) โ†’ 
--         AnnotatedExp (pc โงบ pc') $ ELet x eโ‚ $ AnnotatedExp pc' eโ‚‚
--   , mix $ Infr (๐•Ÿ 5) $ do
--       (pc,()) โ† pCaptureFull $ 
--         parSurroundOptionalWhitespace $ pShaped "+" $ view $ kPPlusL โŒพ tKeywordPunctuationL
--       return $ \ (AnnotatedExp pcโ‚ eโ‚) (AnnotatedExp pcโ‚‚ eโ‚‚) โ†’
--         AnnotatedExp (pcโ‚ โงบ pc โงบ pcโ‚‚) $ EOp Plus (AnnotatedExp pcโ‚ eโ‚) (AnnotatedExp pcโ‚‚ eโ‚‚)
--   , mix $ Inf (๐•Ÿ 5) $ do
--       (pc,()) โ† pCaptureFull $ 
--         parSurroundOptionalWhitespace $ pShaped "-" $ view $ kPMinusL โŒพ tKeywordPunctuationL
--       return $ \ (AnnotatedExp pcโ‚ eโ‚) (AnnotatedExp pcโ‚‚ eโ‚‚) โ†’
--         AnnotatedExp (pcโ‚ โงบ pc โงบ pcโ‚‚) $ EOp Minus (AnnotatedExp pcโ‚ eโ‚) (AnnotatedExp pcโ‚‚ eโ‚‚)
--   , mix $ Infl (๐•Ÿ 100) $ do
--       (pc,()) โ† pCaptureFull $ parWhitespace
--       return $ \ (AnnotatedExp pcโ‚ eโ‚) (AnnotatedExp pcโ‚‚ eโ‚‚) โ†’
--         AnnotatedExp (pcโ‚ โงบ pc โงบ pcโ‚‚) $ EApp (AnnotatedExp pcโ‚ eโ‚) (AnnotatedExp pcโ‚‚ eโ‚‚)
--   ]
-- 
-- parExp โˆท Parser Token ParsedExp
-- parExp = pError "exp" $ mixfixParser parMixes

-- parseStringExpIO โˆท ๐•Š โ†’ IO ParsedExp
-- parseStringExpIO cs = parseIO parExp *$ tokenizeIO tokToken $ tokens cs