415 lines
12 KiB
Haskell
Raw Normal View History

2020-06-17 22:05:44 +04:00
{-# language Strict #-}
2020-06-04 13:48:04 +04:00
{- | /The/ scope resolution system.
-}
2020-06-04 13:48:04 +04:00
module AST.Scope
-- ( HasLocalScope (..)
-- , addLocalScopes
-- , lookupEnv
-- , Kind (..)
-- , ScopedDecl (..)
-- )
2020-06-04 13:48:04 +04:00
where
import Control.Arrow (second)
2020-07-01 16:56:21 +04:00
import Control.Monad.State
2020-08-04 17:31:55 +04:00
import Control.Monad.Catch
import Control.Monad.Catch.Pure
2020-07-01 16:56:21 +04:00
import qualified Data.List as List
2020-07-10 15:11:49 +04:00
import Data.Map (Map)
2020-07-01 16:56:21 +04:00
import qualified Data.Map as Map
2020-07-10 15:11:49 +04:00
import Data.Maybe (listToMaybe)
import Data.Text (Text)
2020-07-01 16:56:21 +04:00
import Duplo.Lattice
import Duplo.Pretty
import Duplo.Tree
2020-07-28 20:00:04 +04:00
import Duplo.Error
import AST.Skeleton
2020-07-28 20:00:04 +04:00
import Parser
2020-07-01 16:56:21 +04:00
import Product
import Range
-- import Debug.Trace
2020-08-04 17:31:55 +04:00
type CollectM = StateT (Product [FullEnv, [Range]]) Catch
type FullEnv = Product ["vars" := Env, "types" := Env]
type Env = Map Range [ScopedDecl]
data Category = Variable | Type
2020-07-28 20:00:04 +04:00
deriving Eq
-- | The type/value declaration.
data ScopedDecl = ScopedDecl
{ _sdName :: LIGO ()
, _sdOrigin :: Range
, _sdBody :: Maybe Range
, _sdType :: Maybe (Either (LIGO ()) Kind)
, _sdRefs :: [Range]
2020-08-07 14:27:07 +04:00
, _sdDoc :: [Text]
}
deriving Show via PP ScopedDecl
2020-08-03 21:31:24 +04:00
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
2020-08-07 14:27:07 +04:00
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
2020-08-03 21:31:24 +04:00
type Info' = Product [[ScopedDecl], Maybe Category, [Text], Range, ShowRange]
2020-08-05 20:31:54 +04:00
-- instance Modifies (Product '[[ScopedDecl], Maybe Category, [Text], Range, a]) where
-- ascribe (ds :> _ :> _ :> r :> _) d =
-- color 3 (fsep (map (pp . _sdName) ds))
-- $$ pp r
-- $$ d
2020-07-28 20:00:04 +04:00
addLocalScopes
2020-08-04 17:31:55 +04:00
:: forall xs
2020-08-07 14:27:07 +04:00
. (Collectable xs, Eq (Product xs))
2020-07-28 20:00:04 +04:00
=> LIGO (Product xs)
-> LIGO (Product ([ScopedDecl] : Maybe Category : xs))
addLocalScopes tree =
fmap (\xs -> fullEnvAt envWithRefs (getRange xs) :> xs) tree1
2020-07-28 20:00:04 +04:00
where
2020-08-04 17:31:55 +04:00
tree0 = either (error . show) id $ runCatch $ unLetRec tree
2020-07-28 20:51:32 +04:00
tree1 = addNameCategories tree0
2020-08-12 14:28:51 +03:00
envWithRefs = getEnvTree tree0
2020-07-28 20:51:32 +04:00
unLetRec
:: forall xs m
2020-08-04 17:31:55 +04:00
. ( MonadCatch m
2020-07-28 20:51:32 +04:00
, Contains Range xs
, Eq (Product xs)
)
=> LIGO (Product xs)
-> m (LIGO (Product xs))
unLetRec = descent leaveBe
[ Descent
2020-08-04 17:31:55 +04:00
\case
(r, Let (layer -> Just (Seq xs)) b) -> maybe (throwM HandlerFailed) return $ convert (getElem r) b xs
_ -> fallthrough
2020-07-28 20:51:32 +04:00
]
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"
2020-07-28 20:51:32 +04:00
delta (Range _ f _) (Range s _ t) = Range s f t
2020-07-28 20:00:04 +04:00
addNameCategories
:: (Contains Range xs, Eq (Product xs))
=> LIGO (Product xs)
-> LIGO (Product (Maybe Category : xs))
2020-08-04 17:31:55 +04:00
addNameCategories tree = evalCollectM do
2020-07-28 20:00:04 +04:00
descent (changeInfo (Nothing :>))
[ Descent
2020-08-04 17:31:55 +04:00
\(r, Name t) -> do
modify $ modElem $ getRange r `addRef` (Variable, t)
return $ (Just Variable :> r, Name t)
2020-07-28 20:00:04 +04:00
, Descent
2020-08-04 17:31:55 +04:00
\(r, TypeName t) -> do
modify $ modElem $ getRange r `addRef` (Type, t)
return $ (Just Type :> r, TypeName t)
2020-07-28 20:00:04 +04:00
]
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
2020-08-04 17:31:55 +04:00
envWithREfs = execCollectM' env do
2020-07-28 20:00:04 +04:00
descent leaveBe
2020-08-04 17:31:55 +04:00
[ 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)
2020-07-28 20:00:04 +04:00
]
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, [])
2020-08-07 14:27:07 +04:00
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
2020-08-04 17:31:55 +04:00
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.
2020-08-07 14:27:07 +04:00
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))
[]
2020-08-07 14:27:07 +04:00
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)
2020-08-07 14:27:07 +04:00
-> [Text]
-> CollectM ()
2020-08-07 14:27:07 +04:00
def name ty body doc = do
define Variable
$ ScopedDecl
(void name)
(getRange $ extract name)
((getRange . extract) <$> body)
((Left . void) <$> ty)
[]
2020-08-07 14:27:07 +04:00
doc
2020-08-07 14:27:07 +04:00
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
2020-08-07 14:27:07 +04:00
instance Collectable xs => Scoped (Product xs) CollectM (LIGO (Product xs)) RawContract where
2020-07-28 20:00:04 +04:00
before r _ = enter r
after _ _ = skip
2020-08-07 14:27:07 +04:00
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
2020-08-07 14:27:07 +04:00
TypeDecl ty body -> defType ty Star body (getElem r)
_ -> enter r
2020-08-07 14:27:07 +04:00
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)
2020-08-07 14:27:07 +04:00
Function recur name _args ty body -> do
leave
unless recur do
def name ty (Just body) (getElem r)
2020-08-07 14:27:07 +04:00
_ -> skip
2020-08-07 14:27:07 +04:00
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
2020-08-07 14:27:07 +04:00
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
2020-08-07 14:27:07 +04:00
def k Nothing Nothing (getElem r)
ForBox k mv _ _ _ -> do
enter r
2020-08-07 14:27:07 +04:00
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
2020-08-07 14:27:07 +04:00
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
2020-08-07 14:27:07 +04:00
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
2020-07-28 20:00:04 +04:00
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
2020-08-12 14:28:51 +03:00
instance Scoped a CollectM (LIGO a) ReasonExpr