module Syntax(module Syntax, Pos, TokenId) where

import Util.Extra(Pos,strChr,strStr)
import SysDeps(PackedString)
import TokenId(TokenId)
import Id(Id)
import Ratio
import Maybe(isNothing,fromJust)
import NT

{- ^
Note that some syntactic constructs contain the syntactic construct
'Type'. However, the rename pass replaces this representation by the internal
type representation 'NewType' and 'NT'. So the syntactic constructs that
use 'Type' are removed by the renaming pass or the type representation is only
half translated @('TokenId' -> 'Id')@. Are the latter still used later?

It probably would have been better if the whole syntax had been parameterised
with respect to the type representation; but such an additional parameter
would also be tiresome.
-}

data Module id =
--     module modid [export] where { impdecls; fixdecls; topdecls }
       Module Pos id (Maybe [Export id]) [ImpDecl id] [FixDecl id] (Decls id)

data Export id =
       ExportEntity  Pos (Entity id)  -- pos superfluous, same in entity?
     | ExportModid   Pos id

data ImpDecl id =
--     import ?qualified? modid ?as modid? ?hiding? (import,..)?
       Import    (Pos,id) (ImpSpec id)
     | ImportQ   (Pos,id) (ImpSpec id)
     | ImportQas (Pos,id) (Pos,id) (ImpSpec id)
     | Importas  (Pos,id) (Pos,id) (ImpSpec id)

importedModule :: ImpDecl a -> a
importedModule (Import (_,id) _) = id
importedModule (ImportQ (_,id) _) = id
importedModule (ImportQas (_,id) _ _) = id
importedModule (Importas (_,id) _ _) = id


data ImpSpec id =
       NoHiding [Entity id]
     | Hiding   [Entity id]

data Entity id =
       EntityVar        Pos id             -- ^ @varid@
     | EntityConClsAll  Pos id             -- ^ @TyCon(..) | TyCls(..)@
     | EntityConClsSome Pos id [(Pos,id)]
          -- ^ @TyCon | TyCls | TyCon(conid,..,conid) | TyCls(varid,..,varid)@

data InfixClass a =
                  InfixDef
                | InfixL
                | InfixR
                | Infix
                | InfixPre a

instance Eq (InfixClass a) where
        InfixDef   == InfixDef   = True
        InfixL     == InfixL     = True
        InfixR     == InfixR     = True
        Infix      == Infix      = True
        (InfixPre _) == (InfixPre _)  = True
        _          == _          = False

instance (Show a) => Show (InfixClass a) where
  showsPrec d InfixDef =  showString "infixl{-def-} "
  showsPrec d InfixL   =  showString "infixl "
  showsPrec d InfixR   =  showString "infixr "
  showsPrec d Infix    =  showString "infix  "
  showsPrec d (InfixPre a) =  showString "prefix " . shows a . showChar ' '


type FixDecl id = (InfixClass id,Int,[FixId id])

data FixId a =
   FixCon Pos a
 | FixVar Pos a

stripFixId :: FixId a -> a
stripFixId (FixCon _ a) = a
stripFixId (FixVar _ a) = a


data Decls id =
   DeclsParse [Decl id]
 | DeclsScc [DeclsDepend id]

-- used very often:
noDecls :: Decls id
noDecls = DeclsParse []


data DeclsDepend id =
       DeclsNoRec (Decl id)
     | DeclsRec   [Decl id]

data Decl id =
       -- | for type synonym: type   simple  = type
       DeclType (Simple id) (Type id)
       -- | renamer replaces DeclType by this.
       --   the type is in the symbol table, referenced by Id
     | DeclTypeRenamed Pos Id   -- intentionally not "id"

       -- | {Nothing = newtype, Just False = data, Just True = data unboxed}
       --   context => simple = constrs
       --   deriving (tycls)
     | DeclData (Maybe Bool) [Context id] (Simple id) [Constr id] [(Pos,id)]
       -- | data primitive conid size
     | DeclDataPrim Pos id Int
       -- | Introduced by Rename to mark that we might need
       --   to generate selector functions
       --       position data\/dataprim [(field,selector)]
     | DeclConstrs Pos id [(Pos,id,id)]
       -- | class context => class where { signatures\/valdefs; }
       --   position, context, class, type variables, fundeps, method decls
     | DeclClass Pos [Context id] id [id] [FunDep id] (Decls id)
       -- | instance context => tycls inst where { valdefs }
     | DeclInstance Pos [Context id] id [Instance id] (Decls id)
       -- | default (type,..)
     | DeclDefault [Type id]
       -- | var primitive arity :: type
     | DeclPrimitive Pos id Int (Type id)
       -- | foreign import [callconv] [extfun] [unsafe|cast|noproto] var :: type
       --                                   (final id part is wrapper-fn for IO)
       --               callconv extfun intfun arity [u|c|n] ty wrapperId
     | DeclForeignImp Pos CallConv String id Int Safety (Type id) id
       -- | foreign export  callconv  [extfun]  var :: type
     | DeclForeignExp Pos CallConv String id (Type id)
       -- | vars :: context => type
     | DeclVarsType [(Pos,id)] [Context id] (Type id)
     | DeclPat (Alt id)
     | DeclFun Pos id [Fun id] -- "var = ..." is a DeclFun, not a DeclPat
--   | DeclSelect id Int id
       -- ^ introduced with pattern elimination (id = select Int id)
--     Used for unimplemented things
     | DeclIgnore String
     | DeclError String
     | DeclAnnot (Decl id) [Annot id]
--   | DeclPragma String String
     -- | infix[rl] int id,..,id
     | DeclFixity (FixDecl id)

-- | for foreign imports\/exports
data Safety = Unsafe | Safe
instance Show Safety where
  showsPrec _ Unsafe     = showString "unsafe"
  showsPrec _ Safe       = id

-- | supported foreign calling conventions
data CallConv = C | Cast | Noproto | Haskell | Other String deriving Eq
instance Show CallConv where
  showsPrec _ C         = showString "ccall"
  showsPrec _ Cast      = showString "cast"
  showsPrec _ Noproto   = showString "noproto"
  showsPrec _ Haskell   = showString "haskell"
  showsPrec _ (Other s) = showString s


-- | introduced by RmClasses
data ClassCode ctx id = 
   CodeClass Pos id  -- ^ class id
 | CodeInstance Pos id id [id] [ctx] [id]
   -- class id, typ id, args, ctxs, method ids

-- | We parse MPTC with functional dependencies, only for hat-trans.
data FunDep id = [id] :->: [id]


data Annot id = AnnotArity (Pos,id) Int
              | AnnotPrimitive (Pos,id) PackedString
              | AnnotNeed [[id]]
              | AnnotUnknown

-- |               lhs pats, guarded exprs,   local defs
data Fun id = Fun  [Pat id] (Rhs id) (Decls id)

funArity :: Fun id -> Int
funArity = length . \(Fun ps _ _) -> ps

data Alt id = Alt  (Pat id) (Rhs id) (Decls id)

data Rhs id = Unguarded (Exp id)
            | PatGuard [([Qual id],Exp id)]
			-- ^ the list has at least one element

data Type id =
       TypeCons         Pos id [Type id]
     | TypeApp          (Type id) (Type id)
     | TypeVar          Pos id
     | TypeStrict       Pos (Type id)

data Sig id = Sig [(Pos,id)] (Type id)  -- for interface file?

data Simple id = Simple Pos id [(Pos,id)]

simpleToType :: Simple id -> Type id
simpleToType (Simple pos tcId pargs) =
  TypeCons pos tcId (map (TypeVar pos . snd) pargs)

data Context id = Context Pos id [(Pos,id)]


{- |
Data constructor applied to type variables, possibly with field names.
As appearing on right hand side of data or newtype definition.

* ConstrCtx is always used if forall is specified

* the intention is to remove Constr completely when all of nhc13 have been updated

* this isn't nhc13, but rather Yhc

* we have comments on the fields here, but can't make them docstrings without record syntax
-}
data Constr id = Constr
                   Pos       -- position of data constructor
                   id        -- data constructor
                   [(Maybe [(Pos,id)],Type id)]
                   -- argumentlist with field labels if any
                   -- (many field labels with same type possible)
                   -- the type admits impossible arguments:
                   -- either all arguments have field names or none
               | ConstrCtx
                   [(Pos,id)]     -- type variabes from forall
                   [Context id]   -- context of data constructor
                   Pos
                   id
                   [(Maybe [(Pos,id)],Type id)]

getConstrId :: Constr id -> id
getConstrId (Constr _ id _) = id
getConstrId (ConstrCtx _ _ _ id _) = id

getConstrArgumentList :: Constr id -> [(Maybe [(Pos,id)],Type id)]
getConstrArgumentList (Constr _ _ xs) = xs
getConstrArgumentList (ConstrCtx _ _ _ _ xs) = xs

getConstrLabels :: Constr id -> [(Pos,id)]
getConstrLabels constr =
  if null args || (isNothing . fst . head) args
    then []
    else concatMap (fromJust . fst) args
  where
  args = getConstrArgumentList constr

getConstrArgumentTypes :: Constr id -> [Type id]
getConstrArgumentTypes constr =
  concat . map (\(l,t) -> replicate (times l) t) . getConstrArgumentList $
    constr
  where
  times Nothing = 1
  times (Just labels) = length labels

constrArity :: Constr id -> Int
constrArity = length . getConstrArgumentTypes


type Instance id = Type id  -- ^ Not 'TypeVar'


{- |
The following is ismorphic to the type constructor 'Qual'.
Possibly Stmt should be removed and its usage replaced everywhere by 'Qual'.
-}
data Stmt id =
    StmtExp  (Exp id)           -- ^ @exp@
  | StmtBind (Exp id) (Exp id)  -- ^ @pat <- exp@
  | StmtLet (Decls id)          -- ^ @let { decls ; }@


type Pat id = Exp id

-- | used both for expressions and patterns
data Exp id =  
      ExpScc            String (Exp id)
      -- ^ never used! should probably be removed
    | ExpDict           (Exp id)         -- ^ hack to mark dictionary arguments
    | ExpLambda         Pos [(Pat id)] (Exp id)  -- ^ @\ pat ... pat -> exp@
    | ExpLet            Pos (Decls id) (Exp id)  -- ^ @let { decls ; } in exp@
    | ExpDo             Pos [Stmt id]            -- ^ @do { stmts ; }@
    | ExpCase           Pos (Exp id) [Alt id]    -- ^ @case exp of { alts; }@
    | ExpFatbar         (Exp id) (Exp id)
      -- ^ never used! should probably be removed
    | ExpFail
      -- ^ never used! should probably be removed
    | ExpIf             Pos (Exp id) (Exp id) (Exp id)
                        -- ^ @if exp then exp else exp@
    | ExpType           Pos (Exp id) [Context id] (Type id)
                        -- ^ exp :: context => type
-- next two are sugared lists; introduced for hpc-trans
    | ExpListComp       Pos (Exp id) [Qual id]
    | ExpListEnum       Pos (Exp id) (Maybe (Exp id)) (Maybe (Exp id))
--- All above only in expressions, not in patterns
--- Below in patterns + expressions.
    | ExpRecord	        (Exp id) [Field id]
    | ExpApplication    Pos [Exp id] -- always at least two elements?
    | ExpVar            Pos id
    | ExpCon            Pos id
    | ExpInfixList      Pos [Exp id] -- Temporary, introduced by parser because
    | ExpVarOp          Pos id       -- it does not know precedence and
    | ExpConOp          Pos id       -- associativity; removed by rename
    | ExpLit            Pos (Lit Boxed)
    | ExpList           Pos [Exp id]
-- bracketed expression mainly for hpc-trans (need accurate pos)
    | ExpBrack          Pos (Exp id)
--- after typechecker
    | Exp2              Pos id id  -- ^ e.g.   Ord.Eq      or Eq.Int
--- Below only in patterns
    | PatAs             Pos id (Pat id)
    | PatWildcard       Pos
    | PatIrrefutable    Pos (Pat id)
-- idea: f (n+k) = exp[n]
--  =>   f n' | k <= n' = exp[n]
--         where n = n'-k
-- (n+k) pattern - store:   n  n' k        (k<=n')  (n'-k)
    | PatNplusK         Pos id id (Exp id) (Exp id) (Exp id)

    -- | typeRep introduced by the type check selection
    | ExpTypeRep        Pos NT

data Field id = FieldExp  Pos id (Exp id)
              | FieldPun  Pos id     -- ^ H98 removes (retained for error msgs)

data Boxed = Boxed | UnBoxed

instance Eq Boxed where
  Boxed == Boxed = True
  UnBoxed == UnBoxed = True
  _     == _       = False

instance Show Boxed where
  showsPrec d Boxed   = id
  showsPrec d UnBoxed = showChar '#'


data Lit boxed =
      LitInteger  boxed Integer
    | LitRational boxed Rational
    | LitString   boxed String
    | LitInt      boxed Int
    | LitDouble   boxed Double
    | LitFloat    boxed Float
    | LitChar     boxed Char

instance (Eq b) => Eq (Lit b) where
     a == a' = litEqual a a'   -- litEqual needed in Symbols to force correct type in gofer

-- seems to be natural equality, why not data Lit (deriving Eq)?
litEqual :: Eq b => Lit b -> Lit b -> Bool
litEqual (LitInteger  b i) (LitInteger  b' i') = i == i' && b == b'
litEqual (LitRational b i) (LitRational b' i') = i == i' && b == b'
litEqual (LitString   b s) (LitString   b' s') = s == s' && b == b'
litEqual (LitInt      b i) (LitInt      b' i') = i == i' && b == b'
litEqual (LitDouble   b f) (LitDouble   b' f') = f == f' && b == b'
litEqual (LitFloat    b f) (LitFloat    b' f') = f == f' && b == b'
litEqual (LitChar     b c) (LitChar     b' c') = c == c' && b == b'
litEqual _                  _            = False

instance (Show b) => Show (Lit b) where
  showsPrec d lit = litshowsPrec d lit  -- litshowsPrec needed in Symbols to force correct type in gofer

litshowsPrec :: (Show b) => Int -> Lit b -> ShowS
litshowsPrec d (LitInteger  b i) = showParen (i<0) (showsPrec d i) . shows b
litshowsPrec d (LitRational b i) =
  -- this is a hack to show a rational in floating point representation
  -- precision might be lost
  -- therer is no library function to print a rational in full precision
  -- in floating point representation
  showParen (i<0) (showsPrec d ((fromRational i)::Double)) . shows b
litshowsPrec d (LitString b str)= showString (strStr str) . shows b
litshowsPrec d (LitInt    b i)  = showParen (i<0) (showsPrec d i) . shows b
litshowsPrec d (LitDouble b f)  = showParen (f<0) (showsPrec d f) . shows b
litshowsPrec d (LitFloat  b f)  = showParen (f<0) (showsPrec d f) . shows b
litshowsPrec d (LitChar   b chr)= showString (strChr chr). shows b

data Qual id =
--       @pat <- exp@
      QualPatExp (Pat id) (Exp id)
--       @pat@
    | QualExp (Exp id)
--       @let decls@
    | QualLet (Decls id)


--------------------

data Interface id
--    interface modid where {iimpdecl; fixdecl; itopdecl }
    = Interface Pos id [IImpDecl id] [FixDecl id] (IDecls id)

type IImpDecl id = ImpDecl id -- No Hiding in ImpSpec

type IDecls id = Decls id -- No Valdef

