----------------------------------------------------------------------
-- |
-- Module      : Grammar
-- Maintainer  : AR
-- Stability   : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/04/21 16:22:20 $ 
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.8 $
--
-- GF source abstract syntax used internally in compilation.
--
-- AR 23\/1\/2000 -- 30\/5\/2001 -- 4\/5\/2003
-----------------------------------------------------------------------------

module GF.Grammar.Grammar (
        -- ** Grammar modules
        Grammar, ModuleName, Module, ModuleInfo(..),
        SourceGrammar, SourceModInfo, SourceModule,
        ModuleType(..),
        emptyGrammar, mGrammar, modules, prependModule, moduleMap,

        MInclude (..), OpenSpec(..),
        extends, isInherited, inheritAll,
        openedModule, allDepsModule, partOfGrammar, depPathModule,
        allExtends, allExtendsPlus, --searchPathModule,
        
        lookupModule,
        isModAbs, isModRes, isModCnc,
        sameMType, isCompilableModule, isCompleteModule,
        allAbstracts, greatestAbstract, allResources,
        greatestResource, allConcretes, allConcreteModules,
        abstractOfConcrete,

        ModuleStatus(..),

        -- ** Judgements
        Info(..),
        -- ** Terms
        Term(..),
        Type,
        Cat,
        Fun,
        QIdent,
        BindType(..),
        Patt(..),
        TInfo(..),
        Label(..),
        MetaId,
        Hypo,
        Context,
        Equation,
        Labelling,
        Assign,
        Option,
        Case,
        LocalDef,
        Param,
        Altern,
        Substitution,
        varLabel, tupleLabel, linLabel, theLinLabel,
        ident2label, label2ident,
        -- ** Source locations
        Location(..), L(..), unLoc, noLoc, ppLocation, ppL,

        -- ** PMCFG        
        LIndex,LVar,LParam(..),PArg(..),Symbol(..),Rule(..)
        ) where

import GF.Infra.Ident
import GF.Infra.Option ---
import GF.Infra.Location

import GF.Data.Operations

import PGF2(BindType(..),PGF)
import PGF2.Transactions(LIndex,LVar,LParam(..),PArg(..),Symbol(..),Rule(..))

import Data.Graph
import Data.Array.IArray(Array)
import Data.Array.Unboxed(UArray)
import qualified Data.Map as Map
import qualified Data.Sequence as Seq
import GF.Text.Pretty


-- | A grammar is a self-contained collection of grammar modules
data Grammar = MGrammar { 
    moduleMap :: Map.Map ModuleName ModuleInfo,
    modules   :: [Module]
  }

-- | Modules
type Module = (ModuleName, ModuleInfo)

data ModuleInfo
 = ModInfo {
    mtype   :: ModuleType,
    mstatus :: ModuleStatus,
    mflags  :: Options,
    mextend :: [(ModuleName,MInclude)],
    mwith   :: Maybe (ModuleName,MInclude,[(ModuleName,ModuleName)]),
    mopens  :: [OpenSpec],
    mexdeps :: [ModuleName],
    msrc    :: FilePath,
    jments  :: Map.Map Ident Info
   }
 | ModPGF {
    mpgf :: PGF
   }

type SourceGrammar = Grammar
type SourceModule = Module
type SourceModInfo = ModuleInfo

instance HasSourcePath ModuleInfo where sourcePath = msrc

-- | encoding the type of the module
data ModuleType = 
    MTAbstract 
  | MTResource
  | MTConcrete ModuleName
  | MTInterface
  | MTInstance (ModuleName,MInclude)
  deriving (Eq,Show)

data MInclude = MIAll | MIOnly [Ident] | MIExcept [Ident]
  deriving (Eq,Show)

extends :: ModuleInfo -> [ModuleName]
extends = map fst . mextend

isInherited :: MInclude -> Ident -> Bool
isInherited c i = case c of
  MIAll -> True
  MIOnly is -> elem i is
  MIExcept is -> notElem i is

inheritAll :: ModuleName -> (ModuleName,MInclude)
inheritAll i = (i,MIAll)

data OpenSpec = 
   OSimple ModuleName
 | OQualif ModuleName ModuleName
  deriving (Eq,Show)

data ModuleStatus = 
   MSComplete 
 | MSIncomplete 
  deriving (Eq,Ord,Show)

openedModule :: OpenSpec -> ModuleName
openedModule o = case o of
  OSimple m -> m
  OQualif _ m -> m

-- | initial dependency list
depPathModule :: ModuleInfo -> [OpenSpec]
depPathModule m = fors m ++ exts m ++ mopens m
  where
    fors m = 
      case mtype m of
        MTConcrete i   -> [OSimple i]
        MTInstance (i,_)   -> [OSimple i]
        _              -> []
    exts m = map OSimple (extends m)

-- | all dependencies
allDepsModule :: Grammar -> ModuleInfo -> [OpenSpec]
allDepsModule gr m = iterFix add os0 where
  os0 = depPathModule m
  add os = [m | o <- os, Just n <- [lookup (openedModule o) mods], 
                m <- depPathModule n]
  mods = modules gr

-- | select just those modules that a given one depends on, including itself
partOfGrammar :: Grammar -> Module -> Grammar
partOfGrammar gr (i,m) = mGrammar [mo | mo@(j,_) <- mods, elem j modsFor]
  where
    mods = modules gr
    modsFor = (i:) $ map openedModule $ allDepsModule gr m

-- | all modules that a module extends, directly or indirectly, with restricts
allExtends :: Grammar -> ModuleName -> [Module]
allExtends gr m =
  case lookupModule gr m of
    Ok mi -> (m,mi) : concatMap (allExtends gr . fst) (mextend mi)
    _     -> []

-- | the same as 'allExtends' plus that an instance extends its interface
allExtendsPlus :: Grammar -> ModuleName -> [ModuleName]
allExtendsPlus gr i =
  case lookupModule gr i of
    Ok m -> i : concatMap (allExtendsPlus gr) (exts m)
    _    -> []
  where
    exts m = extends m ++ [j | MTInstance (j,_) <- [mtype m]]

-- -- | initial search path: the nonqualified dependencies
-- searchPathModule :: ModuleInfo -> [ModuleName]
-- searchPathModule m = [i | OSimple i <- depPathModule m]

prependModule :: Grammar -> Module -> Grammar
prependModule (MGrammar mm ms) im@(i,m) = MGrammar (Map.insert i m mm) (im:ms)

emptyGrammar = mGrammar []

mGrammar :: [Module] -> Grammar
mGrammar ms = MGrammar (Map.fromList ms) ms


-- | we store the module type with the identifier

abstractOfConcrete :: ErrorMonad m => Grammar -> ModuleName -> m ModuleName
abstractOfConcrete gr c = do
  n <- lookupModule gr c
  case mtype n of
    MTConcrete a -> return a
    _ -> raise $ render ("expected concrete" <+> c)

lookupModule :: ErrorMonad m => Grammar -> ModuleName -> m ModuleInfo
lookupModule gr m = case Map.lookup m (moduleMap gr) of
  Just i  -> return i
  Nothing -> raise $ render ("unknown module" <+> m <+> "among" <+> hsep (map fst (modules gr)))

isModAbs :: ModuleInfo -> Bool
isModAbs m =
  case mtype m of
    MTAbstract -> True
    _          -> False

isModRes :: ModuleInfo -> Bool
isModRes m =
  case mtype m of
    MTResource   -> True
    MTInterface  -> True ---
    MTInstance _ -> True
    _            -> False

isModCnc :: ModuleInfo -> Bool
isModCnc m =
  case mtype m of
    MTConcrete _ -> True
    _            -> False

sameMType :: ModuleType -> ModuleType -> Bool
sameMType m n =
  case (n,m) of
    (MTConcrete _, MTConcrete _) -> True

    (MTInstance _, MTInstance _) -> True
    (MTInstance _, MTResource)   -> True
    (MTInstance _, MTConcrete _) -> True

    (MTInterface,  MTInstance _) -> True
    (MTInterface,  MTResource)   -> True    -- for reuse
    (MTInterface,  MTAbstract)   -> True    -- for reuse
    (MTInterface,  MTConcrete _) -> True    -- for reuse

    (MTResource,   MTInstance _) -> True
    (MTResource,   MTConcrete _) -> True    -- for reuse

    _                            -> m == n

-- | don't generate code for interfaces and for incomplete modules
isCompilableModule :: ModuleInfo -> Bool
isCompilableModule m =
  case mtype m of
    MTInterface -> False
    _           -> mstatus m == MSComplete

-- | interface and "incomplete M" are not complete
isCompleteModule :: ModuleInfo -> Bool
isCompleteModule m = mstatus m == MSComplete && mtype m /= MTInterface


-- | all abstract modules sorted from least to most dependent
allAbstracts :: Grammar -> [ModuleName]
allAbstracts gr =
 let scc = stronglyConnComp [(mn,mn,extends mo) | (mn,mo) <- modules gr, mtype mo == MTAbstract]
 in case [mns | CyclicSCC mns <- scc] of
      []     -> [mn | AcyclicSCC mn <- scc]
      cycles -> error $ render ("Cyclic abstract modules:" <+> vcat (map hsep cycles))

-- | the last abstract in dependency order (head of list)
greatestAbstract :: Grammar -> Maybe ModuleName
greatestAbstract gr =
  case allAbstracts gr of
    [] -> Nothing
    as -> return $ last as

-- | all resource modules
allResources :: Grammar -> [ModuleName]
allResources gr = [i | (i,m) <- modules gr, isModRes m || isModCnc m]

-- | the greatest resource in dependency order
greatestResource :: Grammar -> Maybe ModuleName
greatestResource gr =
  case allResources gr of
    [] -> Nothing
    mo:_ -> Just mo ---- why not last as in Abstract? works though AR 24/5/2008

-- | all concretes for a given abstract
allConcretes :: Grammar -> ModuleName -> [ModuleName]
allConcretes gr a =
  [i | (i, m) <- modules gr, mtype m == MTConcrete a, isCompleteModule m]

-- | all concrete modules for any abstract
allConcreteModules :: Grammar -> [ModuleName]
allConcreteModules gr =
  [i | (i, m) <- modules gr, MTConcrete _ <- [mtype m], isCompleteModule m]


-- | the constructors are judgements in 
--
--   - abstract syntax (/ABS/)
-- 
--   - resource (/RES/)
--
--   - concrete syntax (/CNC/)
--
-- and indirection to module (/INDIR/)
data Info =
-- judgements in abstract syntax
   AbsCat   (Maybe (L Context))                         -- ^ (/ABS/) context of a category
 | AbsFun   (Maybe (L Type)) (Maybe (Int,[L Equation])) -- ^ (/ABS/) type, arrity and definition of a function

-- judgements in resource
 | ResParam (Maybe (L [Param])) (Maybe ([Term],Int)) -- ^ (/RES/) The second argument is list of all possible values
                                                     -- and its precomputed length.
 | ResValue (L Type) Int                             -- ^ (/RES/) to mark parameter constructors for lookup.
                                                     -- The second argument is the offset into the list of all values
                                                     -- where that constructor appears first.
 | ResOper  (Maybe (L Type)) (Maybe (L Term))        -- ^ (/RES/)

 | ResOverload [ModuleName] [(L Type,L Term)]        -- ^ (/RES/) idents: modules inherited

-- judgements in concrete syntax
 | CncCat  (Maybe (L Type))                     (Maybe (L Term)) (Maybe (L Term)) (Maybe (L Term)) (Maybe ([Rule],[Rule])) -- ^ (/CNC/) lindef ini'zed, 
 | CncFun  (Maybe ([Ident],Ident,Context,Type)) (Maybe (L Term))                  (Maybe (L Term)) (Maybe [Rule]) -- ^ (/CNC/) type info added at 'TC'

-- indirection to module Ident
 | AnyInd Bool ModuleName                        -- ^ (/INDIR/) the 'Bool' says if canonical
  deriving (Eq,Show)

type Type    = Term
type Cat     = QIdent
type Fun     = QIdent

type QIdent = (ModuleName,Ident)

data Term =
   Vr Ident                      -- ^ variable
 | Cn Ident                      -- ^ constant
 | Con Ident                     -- ^ constructor
 | Sort Ident                    -- ^ basic type
 | EInt Integer                  -- ^ integer literal
 | EFloat Double                 -- ^ floating point literal
 | K String                      -- ^ string literal or token: @\"foo\"@
 | Empty                         -- ^ the empty string @[]@

 | App Term Term                 -- ^ application: @f a@
 | Abs BindType Ident Term       -- ^ abstraction: @\x -> b@
 | Meta {-# UNPACK #-} !MetaId   -- ^ metavariable: @?i@ (only parsable: ? = ?0)
 | ImplArg Term                  -- ^ placeholder for implicit argument @{t}@
 | Prod BindType Ident Term Term -- ^ function type: @(x : A) -> B@, @A -> B@, @({x} : A) -> B@
 | Typed Term Term               -- ^ type-annotated term
--
-- /below this, the constructors are only for concrete syntax/
 | Example Term String           -- ^ example-based term: @in M.C "foo"
 | RecType [Labelling]           -- ^ record type: @{ p : A ; ...}@
 | R [Assign]                    -- ^ record:      @{ p = a ; ...}@
 | P Term Label                  -- ^ projection:  @r.p@
 | ExtR Term Term                -- ^ extension:   @R ** {x : A}@ (both types and terms)

 | Opts Term [Option]            -- ^ options:     @options s in { e => x ; ... }@

 | Table Term Term               -- ^ table type:  @P => A@
 | T TInfo [Case]                -- ^ table:       @table {p => c ; ...}@
 | V Type [Term]                 -- ^ table given as course of values: @table T [c1 ; ... ; cn]@
 | S Term Term                   -- ^ selection:   @t ! p@

 | Let LocalDef Term             -- ^ local definition: @let {t : T = a} in b@

 | Q  QIdent                     -- ^ qualified constant from a package
 | QC QIdent                     -- ^ qualified constructor from a package

 | C Term Term                   -- ^ concatenation: @s ++ t@
 | Glue Term Term                -- ^ agglutination: @s + t@

 | EPatt Int (Maybe Int) Patt    -- ^ pattern (in macro definition): # p
 | EPattType Term                -- ^ pattern type: pattern T

 | ELincat Ident Term            -- ^ boxed linearization type of Ident
 | ELin Ident Term               -- ^ boxed linearization of type Ident

 | FV [Term]                     -- ^ alternatives in free variation: @variants { s ; ... }@

 | Markup Ident [(Ident,Term)] [L Term]
 | Reset Ident (Maybe Term) Term (Maybe QIdent)

 | Alts Term [(Term, Term)]      -- ^ alternatives by prefix: @pre {t ; s\/c ; ...}@
 | Strs [Term]                   -- ^ conditioning prefix strings: @strs {s ; ...}@
 | TSymCat Int LIndex [(LIndex,(Ident,Type))]
 | TSymVar Int Int
  deriving (Show, Eq, Ord)

-- | Patterns
data Patt =
   PC Ident [Patt]        -- ^ constructor pattern: @C p1 ... pn@    @C@ 
 | PP QIdent [Patt]       -- ^ package constructor pattern: @P.C p1 ... pn@    @P.C@ 
 | PV Ident               -- ^ variable pattern: @x@ or wild card @_@
 | PR [(Label,Patt)]      -- ^ record pattern: @{r = p ; ...}@  -- only concrete
 | PString String         -- ^ string literal pattern: @\"foo\"@  -- only abstract
 | PInt    Integer        -- ^ integer literal pattern: @12@    -- only abstract
 | PFloat Double          -- ^ float literal pattern: @1.2@    -- only abstract
 | PT Type Patt           -- ^ type-annotated pattern

 | PAs Ident Patt         -- ^ as-pattern: x@p
 
 | PImplArg Patt          -- ^ placeholder for pattern for implicit argument @{p}@
 | PTilde   Term          -- ^ inaccessible pattern

 -- regular expression patterns
 | PNeg Patt              -- ^ negated pattern: -p
 | PAlt Patt Patt         -- ^ disjunctive pattern: p1 | p2
 | PSeq Int (Maybe Int) Patt Int (Maybe Int) Patt
                          -- ^ sequence of token parts: p + q
                          -- In the constructor PSeq minp maxp p minq maxq q,
                          -- minp/maxp and minq/maxq are the minimal/maximal
                          -- length of a matching string for p/q.
 | PRep Int (Maybe Int) Patt
                          -- ^ repetition of token part: p*
                          -- In the constructor PRep minp maxp p,
                          -- minp/maxp is the minimal/maximal length of
                          -- a matching string for p.
 
 | PChar                  -- ^ string of length one: ?
 | PChars [Char]          -- ^ character list: ["aeiou"]
 | PMacro Ident           -- #p
 | PM QIdent              -- #m.p
  deriving (Show, Eq, Ord)

-- | to guide computation and type checking of tables
data TInfo = 
   TRaw         -- ^ received from parser; can be anything
 | TTyped Type  -- ^ type annontated, but can be anything
 | TComp Type   -- ^ expanded
 | TWild Type   -- ^ just one wild card pattern, no need to expand 
  deriving (Show, Eq, Ord)

-- | record label
data Label = 
    LIdent RawIdent
  | LVar Int
   deriving (Show, Eq, Ord)

type MetaId = Int

type Hypo     = (BindType,Ident,Type)   -- (x:A)  (_:A)  A  ({x}:A)
type Context  = [Hypo]                  -- (x:A)(y:B)   (x,y:A)   (_,_:A)
type Equation = ([Patt],Term) 

type Labelling = (Label, [Ident], Type)
type Assign = (Label, (Maybe Type, Term))
type Option = (Maybe Term, Term)
type Case = (Patt, Term) 
--type Cases = ([Patt], Term) 
type LocalDef = (Ident, (Maybe Type, Term))

type Param = (Ident, Context) 
type Altern = (Term, [(Term, Term)])

type Substitution =  [(Ident, Term)]

varLabel :: Int -> Label
varLabel = LVar

tupleLabel, linLabel :: Int -> Label
tupleLabel i = LIdent $! rawIdentS ('p':show i)
linLabel   i = LIdent $! rawIdentS ('s':show i)

theLinLabel :: Label
theLinLabel = LIdent (rawIdentS "s")

ident2label :: Ident -> Label
ident2label c = LIdent (ident2raw c)

label2ident :: Label -> Ident
label2ident (LIdent s) = identC s
label2ident (LVar i)   = identS ('$':show i)
