2020-08-18 21:56:33 +04:00

415 lines
12 KiB
Haskell

{-# language Strict #-}
{- | /The/ scope resolution system.
-}
module AST.Scope
-- ( HasLocalScope (..)
-- , addLocalScopes
-- , lookupEnv
-- , Kind (..)
-- , ScopedDecl (..)
-- )
where
import Control.Arrow (second)
import Control.Monad.State
import Control.Monad.Catch
import Control.Monad.Catch.Pure
import qualified Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (listToMaybe)
import Data.Text (Text)
import Duplo.Lattice
import Duplo.Pretty
import Duplo.Tree
import Duplo.Error
import AST.Skeleton
import Parser
import Product
import Range
-- import Debug.Trace
type CollectM = StateT (Product [FullEnv, [Range]]) Catch
type FullEnv = Product ["vars" := Env, "types" := Env]
type Env = Map Range [ScopedDecl]
data Category = Variable | Type
deriving Eq
-- | The type/value declaration.
data ScopedDecl = ScopedDecl
{ _sdName :: LIGO ()
, _sdOrigin :: Range
, _sdBody :: Maybe Range
, _sdType :: Maybe (Either (LIGO ()) Kind)
, _sdRefs :: [Range]
, _sdDoc :: [Text]
}
deriving Show via PP ScopedDecl
instance Eq ScopedDecl where
sd == sd1 = and
[ pp (_sdName sd) == pp (_sdName sd1)
, _sdOrigin sd == _sdOrigin sd1
]
-- | The kind.
data Kind = Star
deriving Show via PP Kind
instance {-# OVERLAPS #-} Pretty FullEnv where
pp = block . map aux . Map.toList . mergeFE
where
aux (r, fe) =
pp r `indent` block fe
mergeFE fe = getTag @"vars" @Env fe Prelude.<> getTag @"types" fe
instance Pretty ScopedDecl where
pp (ScopedDecl n o _ t refs doc) = color 3 (pp n) <+> pp o <+> ":" <+> color 4 (maybe "?" (either pp pp) t) <+> "=" <+> pp refs `indent` pp doc
instance Pretty Kind where
pp _ = "TYPE"
instance Pretty Category where
pp Variable = "Variable"
pp Type = "Type"
emptyEnv :: FullEnv
emptyEnv = Tag Map.empty :> Tag Map.empty :> Nil
with :: Category -> FullEnv -> (Env -> Env) -> FullEnv
with Variable env f = modTag @"vars" f env
with Type env f = modTag @"types" f env
ofCategory :: Category -> ScopedDecl -> Bool
ofCategory Variable ScopedDecl { _sdType = Just (Right Star) } = False
ofCategory Variable _ = True
ofCategory Type ScopedDecl { _sdType = Just (Right Star) } = True
ofCategory _ _ = False
type Info' = Product [[ScopedDecl], Maybe Category, [Text], Range, ShowRange]
-- instance Modifies (Product '[[ScopedDecl], Maybe Category, [Text], Range, a]) where
-- ascribe (ds :> _ :> _ :> r :> _) d =
-- color 3 (fsep (map (pp . _sdName) ds))
-- $$ pp r
-- $$ d
addLocalScopes
:: forall xs
. (Collectable xs, Eq (Product xs))
=> LIGO (Product xs)
-> LIGO (Product ([ScopedDecl] : Maybe Category : xs))
addLocalScopes tree =
fmap (\xs -> fullEnvAt envWithRefs (getRange xs) :> xs) tree1
where
tree0 = either (error . show) id $ runCatch $ unLetRec tree
tree1 = addNameCategories tree0
envWithRefs = getEnvTree tree0
unLetRec
:: forall xs m
. ( MonadCatch m
, Contains Range xs
, Eq (Product xs)
)
=> LIGO (Product xs)
-> m (LIGO (Product xs))
unLetRec = descent leaveBe
[ Descent
\case
(r, Let (layer -> Just (Seq xs)) b) -> maybe (throwM HandlerFailed) return $ convert (getElem r) b xs
_ -> fallthrough
]
where
convert :: Range -> LIGO (Product xs) -> [LIGO (Product xs)] -> Maybe (Product xs, Expr (LIGO (Product xs)))
convert r b = match @Expr . linearize r b
linearize :: Range -> LIGO (Product xs) -> [LIGO (Product xs)] -> LIGO (Product xs)
linearize r b [x] = make (modElem @Range (delta r) $ extract x, Let x b)
linearize r b (x : xs) = make (modElem @Range (delta r) $ extract x, Let x (linearize r b xs))
linearize _ _ [] = error "empty Seq"
delta (Range _ f _) (Range s _ t) = Range s f t
addNameCategories
:: (Contains Range xs, Eq (Product xs))
=> LIGO (Product xs)
-> LIGO (Product (Maybe Category : xs))
addNameCategories tree = evalCollectM do
descent (changeInfo (Nothing :>))
[ Descent
\(r, Name t) -> do
modify $ modElem $ getRange r `addRef` (Variable, t)
return $ (Just Variable :> r, Name t)
, Descent
\(r, TypeName t) -> do
modify $ modElem $ getRange r `addRef` (Type, t)
return $ (Just Type :> r, TypeName t)
]
tree
getEnvTree
:: ( Apply (Scoped b CollectM (Tree fs b)) fs
, Apply Foldable fs
, Apply Functor fs
, Apply Traversable fs
, Lattice b
, HasRange b
, Element Name fs
, Element TypeName fs
)
=> Tree fs b
-> FullEnv
getEnvTree tree = envWithREfs
where
envWithREfs = execCollectM' env do
descent leaveBe
[ Descent \(r, Name t) -> do
modify $ modElem $ getRange r `addRef` (Variable, t)
return (r, Name t)
, Descent \(r, TypeName t) -> do
modify $ modElem $ getRange r `addRef` (Type, t)
return (r, TypeName t)
]
tree
env
= execCollectM
$ descent (usingScope' leaveBe) [] tree
fullEnvAt :: FullEnv -> Range -> [ScopedDecl]
fullEnvAt fe r
= envAt (getTag @"types" fe) r
`mappend` envAt (getTag @"vars" fe) r
envAt :: Env -> Range -> [ScopedDecl]
envAt env pos =
Map.elems scopes
where
ranges = List.sortBy partOrder $ filter isCovering $ Map.keys env
scopes = Map.unions $ (map.foldMap) toScopeMap $ map (env Map.!) ranges
isCovering = (pos `leq`)
toScopeMap sd@ScopedDecl {_sdName} = Map.singleton (ppToText _sdName) sd
addRef :: Range -> (Category, Text) -> FullEnv -> FullEnv
addRef r (categ, n) env =
with categ env \slice ->
Map.union
(go slice $ range slice)
slice
where
go slice (r' : rest) =
let decls = slice Map.! r'
in
case updateOnly n r addRefToDecl decls of
(True, decls') -> Map.singleton r' decls'
(False, decls') -> Map.insert r' decls' (go slice rest)
go _ [] = Map.empty
range slice
= List.sortBy partOrder
$ filter (r `leq`)
$ Map.keys slice
addRefToDecl sd = sd
{ _sdRefs = r : _sdRefs sd
}
updateOnly
:: Text
-> Range
-> (ScopedDecl -> ScopedDecl)
-> [ScopedDecl]
-> (Bool, [ScopedDecl])
updateOnly name r f = go
where
go = \case
d : ds
| ppToText (_sdName d) == name ->
if r == _sdOrigin d
then (True, d : ds)
else (True, f d : ds)
| otherwise -> second (d :) (go ds)
[] -> (False, [])
enter :: Collectable xs => Product xs -> CollectM ()
enter r = do
modify $ modElem (getElem @Range r :)
define :: Category -> ScopedDecl -> CollectM ()
define categ sd = do
r <- gets (head . getElem @[Range])
modify
$ modElem @FullEnv \env ->
with categ env
$ Map.insertWith (++) r [sd]
leave :: CollectM ()
leave = modify $ modElem @[Range] tail
-- | Run the computation with scope starting from empty scope.
execCollectM :: CollectM a -> FullEnv
execCollectM = execCollectM' emptyEnv
execCollectM' :: FullEnv -> CollectM a -> FullEnv
execCollectM' env action
= getElem
$ either (error . show) id
$ runCatch
$ execStateT action
$ env :> [] :> Nil
-- | Run the computation with scope starting from empty scope.
evalCollectM :: CollectM a -> a
evalCollectM = evalCollectM' emptyEnv
-- | Run the computation with scope starting from empty scope.
evalCollectM' :: FullEnv -> CollectM a -> a
evalCollectM' env action
= either (error . show) id
$ runCatch
$ evalStateT action
$ env :> [] :> Nil
-- | Search for a name inside a local scope.
lookupEnv :: Text -> [ScopedDecl] -> Maybe ScopedDecl
lookupEnv name = listToMaybe . filter ((name ==) . ppToText . _sdName)
-- | Add a type declaration to the current scope.
defType :: HasRange a => LIGO a -> Kind -> LIGO a -> [Text] -> CollectM ()
defType name kind body doc = do
define Type
$ ScopedDecl
(void name)
(getRange $ extract name)
(Just $ getRange $ extract body)
(Just (Right kind))
[]
doc
-- -- observe :: Pretty i => Pretty res => Text -> i -> res -> res
-- -- observe msg i res
-- -- = traceShow (pp msg, "INPUT", pp i)
-- -- $ traceShow (pp msg, "OUTPUT", pp res)
-- -- $ res
-- | Add a value declaration to the current scope.
def
:: HasRange a
=> LIGO a
-> Maybe (LIGO a)
-> Maybe (LIGO a)
-> [Text]
-> CollectM ()
def name ty body doc = do
define Variable
$ ScopedDecl
(void name)
(getRange $ extract name)
((getRange . extract) <$> body)
((Left . void) <$> ty)
[]
doc
type Collectable xs = (Contains Range xs, Contains [Text] xs)
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) Contract where
before r _ = enter r
after _ _ = skip
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) RawContract where
before r _ = enter r
after _ _ = skip
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) Binding where
before r = \case
Function recur name _args ty body -> do
when recur do
def name ty (Just body) (getElem r)
enter r
TypeDecl ty body -> defType ty Star body (getElem r)
_ -> enter r
after r = \case
Irrefutable name body -> do leave; def name Nothing (Just body) (getElem r)
Var name ty body -> do leave; def name ty (Just body) (getElem r) -- TODO: may be the source of bugs
Const name ty body -> do leave; def name ty (Just body) (getElem r)
Function recur name _args ty body -> do
leave
unless recur do
def name ty (Just body) (getElem r)
_ -> skip
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) VarDecl where
after r (Decl _ name ty) = def name (Just ty) Nothing (getElem r)
instance Scoped a CollectM (LIGO a) Mutable
instance Scoped a CollectM (LIGO a) Type
instance Scoped a CollectM (LIGO a) Variant
instance Scoped a CollectM (LIGO a) TField
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) Expr where
before r = \case
Let {} -> enter r
Lambda {} -> enter r
ForLoop k _ _ _ _ -> do
enter r
def k Nothing Nothing (getElem r)
ForBox k mv _ _ _ -> do
enter r
def k Nothing Nothing (getElem r)
maybe skip (\v -> def v Nothing Nothing (getElem r)) mv
_ -> skip
after _ = \case
Let {} -> leave
Lambda {} -> leave
ForLoop {} -> leave
ForBox {} -> leave
_ -> skip
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) Alt where
before r _ = enter r
after _ _ = leave
instance Scoped a CollectM (LIGO a) LHS
instance Scoped a CollectM (LIGO a) MapBinding
instance Scoped a CollectM (LIGO a) Assignment
instance Scoped a CollectM (LIGO a) FieldAssignment
instance Scoped a CollectM (LIGO a) Constant
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) Pattern where
before r = \case
IsVar n -> def n Nothing Nothing (getElem r)
_ -> skip
instance Scoped a CollectM (LIGO a) QualifiedName
instance Scoped a CollectM (LIGO a) Path
instance Scoped a CollectM (LIGO a) Name
instance Scoped a CollectM (LIGO a) TypeName
instance Scoped a CollectM (LIGO a) FieldName
instance Scoped a CollectM (LIGO a) (Err Text)
instance Scoped a CollectM (LIGO a) Language
instance Scoped a CollectM (LIGO a) Parameters
instance Scoped a CollectM (LIGO a) Ctor
instance Scoped a CollectM (LIGO a) ReasonExpr