385 lines
11 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
2020-07-28 20:00:04 +04:00
import Control.Arrow (first, second)
2020-07-01 16:56:21 +04:00
import Control.Monad.State
2020-07-28 20:51:32 +04:00
import Control.Monad.Identity
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.Sum (Element, Apply, Sum)
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
2020-07-28 20:00:04 +04:00
import AST.Parser
2020-07-01 16:56:21 +04:00
import AST.Types
2020-07-10 15:11:49 +04:00
-- import Comment
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
type CollectM = State (Product [FullEnv, [Range]])
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]
}
deriving Show via PP ScopedDecl
-- | 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) = color 3 (pp n) <+> pp o <+> ":" <+> color 4 (maybe "?" (either pp pp) t) <+> "=" <+> pp refs
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-07-28 20:00:04 +04:00
instance Modifies (Product '[[ScopedDecl], Maybe Category, [Text], Range, a]) where
2020-07-28 20:51:32 +04:00
ascribe (ds :> _ :> _ :> r :> _) d =
color 3 (fsep (map (pp . _sdName) ds))
$$ pp r
2020-07-28 20:00:04 +04:00
$$ d
addLocalScopes
:: (Contains Range xs, Eq (Product xs))
=> LIGO (Product xs)
-> LIGO (Product ([ScopedDecl] : Maybe Category : xs))
addLocalScopes tree =
fmap (\xs -> fullEnvAt envWithREfs (getRange xs) :> xs) tree1
where
2020-07-28 20:51:32 +04:00
tree0 = runIdentity $ unLetRec tree
tree1 = addNameCategories tree0
envWithREfs = getEnvTree tree0
unLetRec
:: forall xs m
. ( Monad 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) -> return $ convert (getElem r) b xs
_ -> return Nothing
]
]
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))
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))
addNameCategories tree = flip evalState emptyEnv do
descent (changeInfo (Nothing :>))
[ Descent
[ \(r, Name t) -> do
-- modify $ getRange r `addRef` (Variable, t)
return $ Just $ (Just Variable :> r, Name t)
]
, Descent
[ \(r, TypeName t) -> do
-- modify $ getRange r `addRef` (Type, t)
return $ Just $ (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 = flip execState env do
descent leaveBe
[ Descent
[ \(r, Name t) -> do
modify $ getRange r `addRef` (Variable, t)
return $ Just (r, Name t)
]
, Descent
[ \(r, TypeName t) -> do
modify $ getRange r `addRef` (Type, t)
return $ Just (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 :: Contains Range 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 action = getElem $ execState action $ emptyEnv :> [] :> 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 -> CollectM ()
defType name kind body = do
define Type
$ ScopedDecl
(void name)
(getRange $ extract name)
(Just $ getRange $ extract body)
(Just (Right kind))
[]
-- -- 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)
-> CollectM ()
def name ty body = do
define Variable
$ ScopedDecl
(void name)
(getRange $ extract name)
((getRange . extract) <$> body)
((Left . void) <$> ty)
[]
instance Contains Range xs => Scoped (Product xs) CollectM (LIGO (Product xs)) Contract where
before r _ = enter r
after _ _ = skip
2020-07-28 20:00:04 +04:00
instance Contains Range xs => Scoped (Product xs) CollectM (LIGO (Product xs)) RawContract where
before r _ = enter r
after _ _ = skip
instance Contains Range 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 (Just ty) (Just body)
enter r
TypeDecl ty body -> defType ty Star body
_ -> enter r
after _ = \case
Irrefutable name body -> do leave; def name Nothing (Just body)
Var name ty body -> do leave; def name (Just ty) (Just body)
Const name ty body -> do leave; def name (Just ty) (Just body)
Function recur name _args ty body -> do
leave
unless recur do
def name (Just ty) (Just body)
_ -> skip
instance Contains Range xs => Scoped (Product xs) CollectM (LIGO (Product xs)) VarDecl where
after _ (Decl _ name ty) = def name (Just ty) Nothing
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 Contains Range 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
ForBox k mv _ _ _ -> do
enter r
def k Nothing Nothing
maybe skip (\v -> def v Nothing Nothing) mv
_ -> skip
after _ = \case
Let {} -> leave
Lambda {} -> leave
ForLoop {} -> leave
ForBox {} -> leave
_ -> skip
instance Contains Range 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 Contains Range xs => Scoped (Product xs) CollectM (LIGO (Product xs)) Pattern where
before _ = \case
IsVar n -> def n Nothing Nothing
_ -> 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