-- |
-- This module provides an implementation of the relapse validation language.
--
-- Relapse is intended to be used for validation of trees or filtering of lists of trees.
--
-- Katydid currently provides two types of trees out of the box: Json and XML, 
-- but relapse supports any type of tree as long the type 
-- is of the Tree typeclass provided by the Parsers module.
--
-- The validate and filter functions expects a Tree to be a list of trees, 
-- since not all serialization formats have a single root.
-- For example, valid json like "[1, 2]" does not have a single root.
-- Relapse can also validate these types of trees.  
-- If your tree has a single root, simply provide a singleton list as input.

module Data.Katydid.Relapse.Relapse (
    parse, parseWithUDFs, Grammar
    , validate, filter
) where

import Prelude hiding (filter)
import Control.Monad.State (runState)
import Control.Monad (filterM)

import Data.Katydid.Parser.Parser

import qualified Data.Katydid.Relapse.Parser as Parser
import qualified Data.Katydid.Relapse.Ast as Ast
import qualified Data.Katydid.Relapse.MemDerive as MemDerive
import qualified Data.Katydid.Relapse.Smart as Smart
import qualified Data.Katydid.Relapse.Exprs as Exprs

-- | Grammar represents a compiled relapse grammar.
newtype Grammar = Grammar Smart.Grammar

-- |
-- parse parses the relapse grammar and returns either a parsed grammar or an error string.
parse :: String -> Either String Grammar
parse grammarString = do {
    parsed <- Parser.parseGrammar grammarString;
    Grammar <$> Smart.compile parsed;
}

-- |
-- parseWithUDFs parses the relapse grammar with extra user defined functions
-- and returns either a parsed grammar or an error string.
parseWithUDFs :: Exprs.MkFunc -> String -> Either String Grammar
parseWithUDFs userLib grammarString = do {
    parsed <- Parser.parseGrammarWithUDFs userLib grammarString;
    Grammar <$> Smart.compile parsed;
}

-- |
-- validate returns whether a tree is valid, given the grammar.
validate :: Tree t => Grammar -> [t] -> Bool
validate g tree = case filter g [tree] of
    [] -> False
    _ -> True

-- |
-- filter returns a filtered list of trees, given the grammar.
filter :: Tree t => Grammar -> [[t]] -> [[t]]
filter (Grammar g) trees =
    let start = Smart.lookupMain g
        f = filterM (MemDerive.validate g start) trees
        (r, _) = runState f MemDerive.newMem
    in r