{-|
Module      : PP.Parsers.Lr
Description : LR parser
Copyright   : (c) 2017 Patrick Champion
License     : see LICENSE file
Maintainer  : chlablak@gmail.com
Stability   : provisional
Portability : portable
-}
module PP.Parsers.Lr
    ( LrConfig(..)
    , LrAst(..)
    , prettyAst
    ) where

import           PP.Builder (LrAction (..), LrTable (..), action, action')
import           PP.Lexer   (OToken (..))
import           PP.Parser  (LrParser (..))
import           PP.Rule    (Rule (..))

-- |Dynamic AST generated by the parser
data LrAst
  = LrAstRoot [LrAst]
  | LrAstTerm [OToken]
  | LrAstNonTerm String [LrAst]
    deriving (Eq, Show)

-- |Configuration for LR parser
data LrConfig = LrConfig
  { lrCount  :: Int        -- ^Counter
  , lrStack  :: [Int]      -- ^State stack
  , lrAction :: LrAction   -- ^Action to do
  , lrInput  :: [OToken]   -- ^Input
  , lrAst    :: LrAst      -- ^Parsed AST
  } deriving (Eq, Show)

-- Dragon Book (2nd edition, fr), page 230, algorithm 4.44
instance LrParser LrConfig where
  config t i = LrConfig 0 [0] (action' t 0 i) i (LrAstRoot [])
  next t (LrConfig c ss (LrShift s) (i:is) a) =
    LrConfig (c + 1) (s : ss) (action' t s is) is (shift a i)
  next t (LrConfig c ss (LrReduce (Rule r xs)) i a) =
    LrConfig (c + 1) sr (action t m $ NonTerm r) i (reduce a r $ length xs - 1)
    where
      sr@(m:_) = drop (length xs - 1) ss
  next t (LrConfig c ss (LrGoto s) i a) =
    LrConfig (c + 1) (s : ss) (action' t s i) i a
  next _ c = c
  hasNext _ (LrConfig _ _ LrError _ _)  = False
  hasNext _ (LrConfig _ _ LrAccept _ _) = False
  hasNext _ _                           = True

-- |Modify the AST by a Shift action
shift :: LrAst -> OToken -> LrAst
shift (LrAstRoot xs) i = LrAstRoot $ xs ++ [LrAstTerm [i]]

-- |Modify the AST by a Reduce action
reduce :: LrAst -> String -> Int -> LrAst
reduce (LrAstRoot xs) r l = LrAstRoot $ a ++ [LrAstNonTerm r b]
  where
    (a, b) = splitAt pos xs
    pos = length xs - l

-- |Pretty print the LrAst
prettyAst :: LrAst -> String
prettyAst (LrAstRoot a) = concatMap (prettyAst' 0) a
  where
    prettyAst' d (LrAstTerm t) = tab d ++ show t ++ "\n"
    prettyAst' d (LrAstNonTerm r xs) =
      tab d ++ r ++ "\n" ++ concatMap (prettyAst' $ d + 2) xs
    tab d = replicate d ' ' ++ "|"