----------------------------------------------------------------------
-- |
-- Maintainer  : PL
-- Stability   : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/05/13 12:40:19 $ 
-- > CVS $Author: peb $
-- > CVS $Revision: 1.9 $
--
-- The main parsing module, parsing GFC grammars
-- by translating to simpler formats, such as PMCFG and CFG
----------------------------------------------------------------------

module GF.Parsing.GFC
    (parse, PInfo(..), buildPInfo) where

import GF.System.Tracing 
import GF.Infra.Print
import qualified GF.Grammar.PrGrammar as PrGrammar

import GF.Data.Operations (Err(..))

import qualified GF.Grammar.Grammar as Grammar
import qualified GF.Grammar.Macros as Macros
import qualified GF.Canon.AbsGFC as AbsGFC
import qualified GF.Infra.Ident as Ident
import GF.CF.CFIdent (CFCat, cfCat2Ident, CFTok, wordsCFTok)

import GF.Data.SortedList 
import GF.Data.Assoc
import GF.Formalism.Utilities
import GF.Conversion.Types

import qualified GF.Formalism.GCFG as G
import qualified GF.Formalism.SimpleGFC as S
import qualified GF.Formalism.MCFG as M
import qualified GF.Formalism.CFG as C
import qualified GF.Parsing.MCFG as PM
import qualified GF.Parsing.CFG as PC

----------------------------------------------------------------------
-- parsing information

data PInfo = PInfo { mcfPInfo :: MCFPInfo,
		     cfPInfo  :: CFPInfo }

type MCFPInfo = PM.MCFPInfo MCat Name MLabel Token
type CFPInfo  = PC.CFPInfo CCat Name Token

buildPInfo :: MGrammar -> CGrammar -> PInfo
buildPInfo mcfg cfg = PInfo { mcfPInfo = PM.buildMCFPInfo mcfg,
			      cfPInfo  = PC.buildCFPInfo cfg }

instance Print PInfo where
    prt (PInfo m c) = prt m ++ "\n" ++ prt c

----------------------------------------------------------------------
-- main parsing function

parse :: String         -- ^ parsing algorithm (mcfg or cfg)
      -> String         -- ^ parsing strategy
      -> PInfo          -- ^ compiled grammars (mcfg and cfg) 
      -> Ident.Ident    -- ^ abstract module name
      -> CFCat          -- ^ starting category
      -> [CFTok]        -- ^ input tokens
      -> Err [Grammar.Term] -- ^ resulting GF terms

parse prs strategy pinfo abs startCat inString = 
    do let inTokens = tracePrt "Parsing.GFC - input tokens" prt $
		      inputMany (map wordsCFTok inString)
       forests <- selectParser prs strategy pinfo startCat inTokens
       traceM "Parsing.GFC - nr. unfiltered forests" (prt (length forests))
       traceM "Parsing.GFC - nr. unfiltered trees" (prt (length (forests >>= forest2trees)))
       let filteredForests = tracePrt "Parsing.GFC - nr. forests" (prt . length) $
			     forests >>= applyProfileToForest
	   -- compactFs = tracePrt "#compactForests" (prt . length) $
	   -- 	          tracePrt "compactForests" (prtBefore "\n") $
	   -- 	          compactForests forests
	   trees = tracePrt "Parsing.GFC - nr. trees" (prt . length) $
		   nubsort $ filteredForests >>= forest2trees
		   -- compactFs >>= forest2trees
       return $ map (tree2term abs) trees


-- parsing via CFG
selectParser "c" strategy pinfo startCat inTokens
    = do let startCats = tracePrt "Parsing.GFC - starting CF categories" prt $
			 filter isStart $ map fst $ aAssocs $ PC.topdownRules cfpi
	     isStart cat = ccat2scat cat == cfCat2Ident startCat
	     cfpi = cfPInfo pinfo
	 cfParser <- PC.parseCF strategy
	 let cfChart = tracePrt "Parsing.GFC - CF chart" (prt . length) $
		       cfParser cfpi startCats inTokens
	     chart = tracePrt "Parsing.GFC - chart" (prt . map (length.snd) . aAssocs) $
		     C.grammar2chart cfChart
	     finalEdges = tracePrt "Parsing.GFC - final chart edges" prt $
			  map (uncurry Edge (inputBounds inTokens)) startCats
	 return $ chart2forests chart (const False) finalEdges

-- parsing via MCFG
selectParser "m" strategy pinfo startCat inTokens
    = do let startCats = tracePrt "Parsing.GFC - starting MCF categories" prt $
			 filter isStart $ PM.grammarCats mcfpi
	     isStart cat = mcat2scat cat == cfCat2Ident startCat
	     mcfpi = mcfPInfo pinfo
	 mcfParser <- PM.parseMCF strategy
	 let mcfChart = tracePrt "Parsing.GFC - MCF chart" (prt . length) $
			mcfParser mcfpi startCats inTokens
	     chart = tracePrt "Parsing.GFC - chart" (prt . length . concat . map snd . aAssocs) $
		     G.abstract2chart mcfChart
	     finalEdges = tracePrt "Parsing.GFC - final chart edges" prt $
			  [ PM.makeFinalEdge cat lbl (inputBounds inTokens) | 
			    cat@(MCat _ [lbl]) <- startCats ]
	 return $ chart2forests chart (const False) finalEdges

-- error parser: 
selectParser prs strategy _ _ _ = Bad $ "Parser '" ++ prs ++ "' not defined with strategy: " ++ strategy 


----------------------------------------------------------------------
-- parse trees to GF terms

tree2term :: Ident.Ident -> SyntaxTree Fun -> Grammar.Term
tree2term abs (TNode f ts) = Macros.mkApp (Macros.qq (abs,f)) (map (tree2term abs) ts)
tree2term abs (TMeta)      = Macros.mkMeta 0


----------------------------------------------------------------------
-- conversion and unification of forests

-- simplest implementation
applyProfileToForest :: SyntaxForest Name -> [SyntaxForest Fun]
applyProfileToForest (FNode name@(Name fun profile) children) 
    | isCoercion name = concat chForests
    | otherwise       = [ FNode fun chForests | not (null chForests) ]
    where chForests   = concat [ applyProfileM unifyManyForests profile forests |
				 forests0 <- children,
				 forests <- mapM applyProfileToForest forests0 ]

{-
-- more intelligent(?) implementation
applyProfileToForest (FNode (Name name profile) children) 
    | isCoercion name = concat chForests
    | otherwise       = [ FNode name chForests | not (null chForests) ]
    where chForests   = concat [ mapM (checkProfile forests) profile |
				 forests0 <- children,
				 forests <- mapM applyProfileToForest forests0 ]
-}


