packages feed

babynf-0.1.0.2: lib/Data/BAByNF/Core/Parseable.hs

module Data.BAByNF.Core.Parseable
    ( Parseable (..)
    , toParser
    , Dict
    , TreeParser
    ) where

import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.List.NonEmpty qualified as List.NonEmpty

import Data.Attoparsec.ByteString qualified as Attoparsec
import Data.BAByNF.Core.Tree (Tree)
import Data.BAByNF.Core.Tree qualified as Tree
import Data.BAByNF.Core.Ref (Ref)
import Data.BAByNF.Core.Ref qualified as Ref
import Data.BAByNF.Core.RefDict (RefDict)
import Data.BAByNF.Core.RefDict qualified as RefDict
import Data.BAByNF.Core.Repeat (Repeat, RepeatCount)
import Data.BAByNF.Core.Repeat qualified as Repeat

type Dict a = RefDict a (Parseable a)
type TreeParser a = Attoparsec.Parser (Tree a)

data ParserEnvironment a where
    ParserEnvironment :: (Ref a) =>  { parserGrammar :: Dict a, parserContextStack :: [ParserContext a] } -> ParserEnvironment a
deriving instance (Show a) => Show (ParserEnvironment a)

data ParserState a where
    ParserState :: (Ref a) => { parserEnvironment ::  ParserEnvironment a , parserFocus :: ParserFocus a } -> ParserState a
deriving instance (Show a) => Show (ParserState a)

data ParserContext a where
    SeqContext :: (Ref a) => { seqPrev :: Tree a, seqNext :: [Parseable a]} -> ParserContext a
    RepContext :: (Ref a) => { repPrev :: Tree a, repParse :: Parseable a, repCount :: RepeatCount} -> ParserContext a
    AltContext :: (Ref a) => { altNext ::  [Parseable a]} -> ParserContext a
    RuleContext :: (Ref a) => { ruleRef :: a } -> ParserContext a
deriving instance (Show a) => Show (ParserContext a)

data Parseable a where
    Seq :: (Ref a) => NonEmpty (Parseable a) -> Parseable a
    Alt :: (Ref a) => NonEmpty (Parseable a) -> Parseable a
    Rep :: (Ref a) => (Parseable a) -> Repeat -> Parseable a
    Rule :: (Ref a) => a -> Parseable a
    Unit :: String -> (TreeParser a) -> Parseable a

instance (Show a) => Show (Parseable a) where
    show :: Parseable a -> String
    show x = case x of
        Seq y -> "Seq( " ++ show (List.NonEmpty.toList y) ++ " )"
        Alt y -> "Alt( " ++ show (List.NonEmpty.toList y) ++ " )"
        Rep a b -> "Rep( {" ++ show (Repeat.required b) ++ "," ++ show (Repeat.optional b) ++  "}->" ++ show a ++ ")"
        Rule r -> "Rule (" ++ show r ++ ")"
        Unit desc _ -> "Unit <" ++ desc ++ ">"

data ParserFocus a where
    Before :: Ref a => Parseable a -> ParserFocus a
    OnReturn :: Ref a => ParserContext a -> Tree a -> ParserFocus a
    OnFailure :: Ref a => ParserContext a -> ParserFocus a
    After :: Ref a => Tree a -> ParserFocus a
deriving instance (Show a) => Show (ParserFocus a)

toParser :: (Ref a, Show a) => Dict a -> Parseable a -> TreeParser a
toParser grammar parseable = toParser' $ ParserState { parserEnvironment = ParserEnvironment { parserGrammar = grammar, parserContextStack = []}, parserFocus = Before (Seq $ parseable :| [Unit "endOfInput" $ Attoparsec.endOfInput >> return Tree.empty])  }

alts :: (Ref a) => [Parseable a] -> Maybe (Parseable a)
alts [] = Nothing
alts [x] = Just x
alts (x:xs) = Just $ Alt (x :| xs)

data Action a where
    Return :: Ref a => Tree a -> Action a
    Split :: Ref a => ParserContext a -> Parseable a -> Action a
    Branch :: Ref a => ParserContext a -> Parseable a -> Action a
    Parse :: Ref a => TreeParser a -> Action a
    Panic :: String -> Action a

applyAction :: (Ref a, Show a) => ParserEnvironment a -> Action a -> TreeParser a
applyAction env (Return tree) =
    case pop env of
        Nothing -> return tree
        Just (env', ctx) -> 
            toParser' ParserState { parserEnvironment = env'
                                  , parserFocus = OnReturn ctx tree 
                                  }
applyAction env (Split ctx p) = 
    let env' = push env ctx 
     in toParser' ParserState { parserEnvironment = env'
                              , parserFocus = Before p  
                              }
applyAction env (Branch ctx p) = 
    Attoparsec.choice [ applyAction env (Split ctx p)
                      , toParser' ParserState { parserEnvironment = env, parserFocus = OnFailure ctx }
                      ]
applyAction env (Parse p) = 
    p >>= \tree -> toParser' ParserState { parserEnvironment = env
                                         , parserFocus = After tree 
                                         }
applyAction _ (Panic withMsg) = fail withMsg

pop :: (Ref a) => ParserEnvironment a -> Maybe (ParserEnvironment a, ParserContext a)
pop ParserEnvironment { parserGrammar = grammar, parserContextStack = contextStack } =
    case contextStack of
        [] -> Nothing
        ctx : rest -> Just (ParserEnvironment { parserGrammar = grammar, parserContextStack = rest }, ctx)

push :: (Ref a) => ParserEnvironment a -> ParserContext a -> ParserEnvironment a
push ParserEnvironment { parserGrammar = grammar, parserContextStack = contextStack} ctx = ParserEnvironment { parserGrammar = grammar, parserContextStack = ctx : contextStack }

toParser' :: (Ref a, Show a) => ParserState a -> TreeParser a
toParser' state =
    let action = case parserFocus state of
            Before (Unit _ p) -> Parse p
            Before (Rule ref) ->
                let maybeP = lookupDef ref (parserEnvironment state)
                 in maybe (Panic $ "undefined " ++ Ref.display ref) (Split RuleContext { ruleRef = ref }) maybeP
            Before (Seq (p :| ps)) -> Split SeqContext { seqPrev = Tree.empty, seqNext = ps } p
            Before (Alt (p :| ps)) -> Branch AltContext { altNext = ps} p
            Before (Rep p rep) ->
                let rc = Repeat.initCount rep
                 in case Repeat.state rc of
                    Repeat.Satisfied -> Return Tree.empty
                    Repeat.WantMore -> Branch RepContext { repPrev = Tree.empty, repParse = p, repCount = rc } p
                    Repeat.NeedMore -> Split RepContext { repPrev = Tree.empty, repParse = p, repCount = rc} p
            OnReturn SeqContext { seqPrev = prev, seqNext = next } tree ->
                case next of
                    [] -> Return $ prev <> tree
                    p : next' -> Split SeqContext { seqPrev = prev <> tree, seqNext = next' } p
            OnReturn AltContext { altNext = _ } tree -> Return tree
            OnReturn RepContext { repParse = p, repPrev = prev, repCount = rc } tree ->
                case Repeat.tryIncrementCount rc of
                    Nothing -> Panic "repetitions already satisfied"
                    Just rc' ->
                        case Repeat.state rc' of
                            Repeat.Satisfied -> Return $ prev <> tree
                            Repeat.WantMore -> Branch RepContext { repPrev = prev <> tree, repParse = p, repCount = rc' } p
                            Repeat.NeedMore -> Split RepContext { repPrev = prev <> tree, repParse = p, repCount = rc' } p
            OnReturn RuleContext { ruleRef = ref } tree -> Return $ Tree.singleton $ Tree.RefNode ref tree
            OnFailure AltContext { altNext = next } ->
                case next of
                    [] -> Panic "no more alts"
                    p : next' -> Branch AltContext { altNext = next' } p
            OnFailure RepContext { repParse = _, repPrev = prev, repCount = rc } ->
                case Repeat.state rc of
                    Repeat.NeedMore -> Panic "more repetitions required"
                    _ -> Return prev
            OnFailure _ -> Panic "failure in non-safeguarded context"
            After tree -> Return tree
     in applyAction (parserEnvironment state) action

lookupDef :: Ref a => a -> ParserEnvironment a -> Maybe (Parseable a)
lookupDef ref env = lookupDef' ref (parserGrammar env)
lookupDef' :: Ref a => a -> Dict a -> Maybe (Parseable a)
lookupDef' ref grammar = alts $ RefDict.lookup ref grammar