module Lang.LamIf.Stamp where

import FP
import Lang.LamIf.Syntax
import Lang.LamIf.Parser

data Name = Name
  { nameID  
  , nameSource  𝕊
  }

instance Eq Name where (==) = () `on` nameID
instance Ord Name where compare = compare `on` nameID
instance Pretty Name where pretty = ppBdr  nameSource

data Exp = Exp
  { expID  
  , expContext  SourceContext Token
  , expRawExp  PreExp Name Exp
  }
type Atom = PreAtom Name Exp

stripStampedPreExp  PreExp Name Exp  PreExp 𝕊 (Fixed (PreExp 𝕊))
stripStampedPreExp = mapExp nameSource nameSource stripStampedExp

stripStampedExp  Exp  Fixed (PreExp 𝕊)
stripStampedExp = Fixed  stripStampedPreExp  expRawExp

instance Eq Exp where (==) = () `on` expID
instance Ord Exp where compare = () `on` expID
instance POrd Exp where () = discretePO

instance Pretty Exp where
  pretty e = ppVertical
    -- [ ppHorizontal [ppHeader "ID:",pretty $ expID e]
    -- , ppHeader "Source:"
    [ pretty $ expContext e
    -- , ppHeader "AST:"
    -- , pretty $ stripStampedExp e
    ]

data StampState = StampState
  { stampNameID  
  , stampExpID  
  , stampBinderMap  𝕊  
  }
makeLenses ''StampState

stampState₀  StampState
stampState₀ = StampState bot bot emptyDict

data StampEnv = StampEnv
  { stampContext  SourceContext Token
  }
makeLenses ''StampEnv

stampEnv₀  StampEnv
stampEnv₀ = StampEnv null

newtype StampM a = StampM { runStampM  ReaderT StampEnv (StateT StampState (Error Doc)) a }
  deriving 
    (Functor,Monad
    ,MonadError Doc
    ,MonadState StampState
    ,MonadReader StampEnv
    )

runStampM₀  StampM a  Doc  a
runStampM₀ xM = runError $ evalStateTWith stampState₀ $ runReaderTWith stampEnv₀ $ runStampM xM

stampExp  SourceExp  StampM Exp
stampExp (SourceExp ctx e) = do
  i  nextL stampExpIDL
  ei  local (update stampContextL ctx) $ mapExpM stampVar stampBinder stampExp e
  return $ Exp i ctx ei

stampVar  𝕊  StampM Name
stampVar x = do
  binderMap  getL stampBinderMapL
  case binderMap # x of
    Nothing  do
      fc  askL stampContextL
      throw $ ppVertical
        [ ppHeader "Bad Syntax: Unbound variable"
        , errorSourceContext fc
        ]
    Just n  return $ Name n x

stampBinder  𝕊  StampM Name
stampBinder x = do
  i  nextL stampNameIDL
  modifyL stampBinderMapL $ insertDict x i
  return $ Name i x

stamp  SourceExp  Doc  Exp
stamp = runStampM₀  stampExp

-- ioError ∘ runStampM₀ ∘ stampExp *$ e_id