2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-04 13:48:04 +04:00
|
|
|
{- | /The/ scope resolution system.
|
2020-05-21 23:28:26 +04:00
|
|
|
-}
|
|
|
|
|
2020-06-04 13:48:04 +04:00
|
|
|
module AST.Scope
|
2020-06-10 22:37:02 +04:00
|
|
|
-- ( -- * Monad
|
|
|
|
-- ScopeM
|
|
|
|
-- , evalScopeM
|
|
|
|
-- , collectEnv
|
|
|
|
|
|
|
|
-- -- * Scope
|
|
|
|
-- , Env(..)
|
|
|
|
-- , ScopedDecl(..)
|
|
|
|
-- , Kind(..)
|
|
|
|
-- , HasEnv(..)
|
|
|
|
-- , lookupEnv
|
|
|
|
|
|
|
|
-- -- * Methods
|
|
|
|
-- , enter
|
|
|
|
-- , leave
|
|
|
|
-- , define
|
|
|
|
-- , defType
|
|
|
|
-- , def
|
|
|
|
-- )
|
2020-06-04 13:48:04 +04:00
|
|
|
where
|
2020-05-21 23:28:26 +04:00
|
|
|
|
|
|
|
import Control.Monad.State
|
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
import qualified Data.Map as Map
|
|
|
|
import Data.Map (Map)
|
|
|
|
import Data.Text (Text)
|
|
|
|
import qualified Data.Text as Text
|
|
|
|
import Data.Maybe (fromJust)
|
2020-05-21 23:28:26 +04:00
|
|
|
|
|
|
|
import Range
|
|
|
|
import AST.Types
|
2020-06-10 22:37:02 +04:00
|
|
|
import AST.Parser
|
|
|
|
import Parser
|
2020-06-01 18:17:33 +04:00
|
|
|
import Tree
|
2020-06-04 19:15:14 +04:00
|
|
|
import Comment
|
2020-06-01 18:17:33 +04:00
|
|
|
import Pretty
|
2020-06-09 15:56:11 +04:00
|
|
|
import Product
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
import Debug.Trace
|
|
|
|
|
2020-06-04 17:40:38 +04:00
|
|
|
-- | Scope-holding monad.
|
2020-06-10 22:37:02 +04:00
|
|
|
type ScopeM = State [Scopes]
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-04 17:40:38 +04:00
|
|
|
-- | Run the computation with scope starting from empty scope.
|
|
|
|
evalScopeM :: ScopeM a -> a
|
2020-06-10 22:37:02 +04:00
|
|
|
evalScopeM action = evalState action []
|
2020-06-04 17:40:38 +04:00
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
type Scopes = Tree '[ScopeTree] Range
|
|
|
|
|
|
|
|
data ScopeTree it
|
|
|
|
= ScopeTree Env [it]
|
|
|
|
deriving stock (Functor, Foldable, Traversable)
|
|
|
|
deriving stock Show
|
|
|
|
|
|
|
|
instance Pretty1 ScopeTree where
|
|
|
|
pp1 (ScopeTree e cs) =
|
|
|
|
pp e `indent` block cs
|
2020-06-09 15:56:11 +04:00
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
instance {-# OVERLAPS #-} Pretty (Map Text ScopedDecl) where
|
|
|
|
pp = block . map aux . Map.toList
|
|
|
|
where
|
|
|
|
aux (n, ScopedDecl o b t rs) =
|
|
|
|
pp o <+> (pp n <> ":") <+> pp t <+> "=" <+> pp b <+> "/" <+> (list rs <> ";")
|
|
|
|
|
|
|
|
-- | The environment.
|
|
|
|
type Env = Map Text ScopedDecl
|
|
|
|
-- deriving Show via PP Env
|
|
|
|
|
|
|
|
-- instance Pretty Env where
|
|
|
|
-- pp = vcat . map pp . _eDecls
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-04 17:40:38 +04:00
|
|
|
-- | The type/value declaration.
|
2020-05-21 23:28:26 +04:00
|
|
|
data ScopedDecl = ScopedDecl
|
2020-06-10 22:37:02 +04:00
|
|
|
{ _sdOrigin :: Range
|
2020-05-21 23:28:26 +04:00
|
|
|
, _sdBody :: Maybe Range
|
2020-06-01 18:17:33 +04:00
|
|
|
, _sdType :: Maybe (Either (Pascal ()) Kind)
|
2020-06-10 22:37:02 +04:00
|
|
|
, _sdRefs :: [Range]
|
2020-05-21 23:28:26 +04:00
|
|
|
}
|
2020-06-10 22:37:02 +04:00
|
|
|
deriving Show via PP ScopedDecl
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-09 15:56:11 +04:00
|
|
|
instance Pretty ScopedDecl where
|
2020-06-10 22:37:02 +04:00
|
|
|
pp (ScopedDecl o b t refs) = pp o <+> "-" <+> maybe "?" (either pp pp) t <+> "=" <+> pp o
|
2020-06-09 15:56:11 +04:00
|
|
|
|
2020-06-04 17:40:38 +04:00
|
|
|
-- | The kind.
|
2020-05-21 23:28:26 +04:00
|
|
|
data Kind = Star
|
2020-06-09 15:56:11 +04:00
|
|
|
deriving Show via PP Kind
|
|
|
|
|
|
|
|
instance Pretty Kind where
|
|
|
|
pp _ = "*"
|
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
lookupEnv :: Text -> Env -> Maybe ScopedDecl
|
|
|
|
lookupEnv name = Map.lookup name
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-04 17:40:38 +04:00
|
|
|
-- | Make a new scope out of enclosing parent one.
|
2020-06-10 22:37:02 +04:00
|
|
|
enter :: Range -> ScopeM ()
|
|
|
|
enter r =
|
|
|
|
modify \rest ->
|
|
|
|
mk r (ScopeTree Map.empty []) : rest
|
2020-06-04 17:40:38 +04:00
|
|
|
|
|
|
|
-- | Leave current scope, return to parent one.
|
|
|
|
leave :: ScopeM ()
|
2020-06-10 22:37:02 +04:00
|
|
|
leave =
|
|
|
|
modify \(a : parent : rest) ->
|
|
|
|
fromJust do
|
|
|
|
(r, ScopeTree e cs) <- match parent
|
|
|
|
return $ mk r (ScopeTree e (a : cs)) : rest
|
2020-06-04 17:40:38 +04:00
|
|
|
|
|
|
|
-- | Add a declaration to the current scope.
|
2020-06-10 22:37:02 +04:00
|
|
|
define :: Text -> ScopedDecl -> ScopeM ()
|
|
|
|
define name d =
|
|
|
|
modify \(top : rest) ->
|
|
|
|
fromJust do
|
|
|
|
(r, ScopeTree a cs) <- match top
|
|
|
|
return $ mk r (ScopeTree (Map.insert name d a) cs) : rest
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-04 17:40:38 +04:00
|
|
|
-- | Add a type declaration to the current scope.
|
2020-06-01 18:17:33 +04:00
|
|
|
defType :: HasRange a => Pascal a -> Kind -> Pascal a -> ScopeM ()
|
|
|
|
defType name kind body = do
|
2020-06-10 22:37:02 +04:00
|
|
|
define (ppToText $ void name)
|
|
|
|
$ ScopedDecl
|
|
|
|
(getRange $ infoOf name)
|
|
|
|
(Just $ getRange $ infoOf body)
|
|
|
|
(Just (Right kind))
|
|
|
|
[]
|
|
|
|
|
|
|
|
addRef :: Text -> Range -> [Scopes] -> [Scopes]
|
|
|
|
addRef _ _ [] = error "addRef: empty env stack"
|
|
|
|
addRef name pos stack@(topmost : _)
|
|
|
|
| Just (r, ScopeTree top cs) <- match topmost =
|
|
|
|
case Map.lookup name top of
|
|
|
|
Just ScopedDecl {_sdOrigin} -> go _sdOrigin stack
|
|
|
|
Nothing -> stack
|
|
|
|
|
|
|
|
where
|
|
|
|
go _ [] = []
|
|
|
|
go range initial@(topmost : rest)
|
|
|
|
| Just (r, ScopeTree e cs) <- match topmost =
|
|
|
|
case Map.lookup name e of
|
|
|
|
Just it | _sdOrigin it == range ->
|
|
|
|
mk r (ScopeTree (pushRef pos e) cs) : go range rest
|
|
|
|
_ ->
|
|
|
|
initial
|
|
|
|
|
|
|
|
pushRef pos' = Map.adjust (\sd -> sd { _sdRefs = pos' : _sdRefs sd }) name
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-04 17:40:38 +04:00
|
|
|
-- | Add a value declaration to the current scope.
|
2020-05-21 23:28:26 +04:00
|
|
|
def
|
2020-06-01 18:17:33 +04:00
|
|
|
:: HasRange a
|
|
|
|
=> Pascal a
|
|
|
|
-> Maybe (Pascal a)
|
|
|
|
-> Maybe (Pascal a)
|
2020-05-21 23:28:26 +04:00
|
|
|
-> ScopeM ()
|
2020-06-01 18:17:33 +04:00
|
|
|
def name ty body = do
|
2020-06-10 22:37:02 +04:00
|
|
|
define (ppToText $ void name)
|
|
|
|
$ ScopedDecl
|
|
|
|
(getRange $ infoOf name)
|
|
|
|
((getRange . infoOf) <$> body)
|
|
|
|
((Left . void) <$> ty)
|
|
|
|
[]
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
instance UpdateOver ScopeM Contract (Pascal a) where
|
|
|
|
before r _ = enter r
|
|
|
|
after _ _ = leave
|
2020-06-01 18:17:33 +04:00
|
|
|
|
|
|
|
instance HasRange a => UpdateOver ScopeM Declaration (Pascal a) where
|
2020-06-10 22:37:02 +04:00
|
|
|
before _ = \case
|
2020-06-01 18:17:33 +04:00
|
|
|
TypeDecl ty body -> defType ty Star body
|
|
|
|
_ -> skip
|
|
|
|
|
|
|
|
instance HasRange a => UpdateOver ScopeM Binding (Pascal a) where
|
2020-06-10 22:37:02 +04:00
|
|
|
before r = \case
|
2020-06-04 17:16:04 +04:00
|
|
|
Function recur name _args ty body -> do
|
2020-05-21 23:28:26 +04:00
|
|
|
when recur do
|
2020-06-01 18:17:33 +04:00
|
|
|
def name (Just ty) (Just body)
|
2020-06-10 22:37:02 +04:00
|
|
|
enter r
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
_ -> enter r
|
2020-05-21 23:28:26 +04:00
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
after _ = \case
|
2020-06-01 18:17:33 +04:00
|
|
|
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)
|
2020-06-04 17:16:04 +04:00
|
|
|
Function recur name _args ty body -> do
|
2020-06-01 18:17:33 +04:00
|
|
|
leave
|
2020-05-21 23:28:26 +04:00
|
|
|
unless recur do
|
2020-06-01 18:17:33 +04:00
|
|
|
def name (Just ty) (Just body)
|
|
|
|
|
|
|
|
instance HasRange a => UpdateOver ScopeM VarDecl (Pascal a) where
|
2020-06-10 22:37:02 +04:00
|
|
|
after _ (Decl _ name ty) = def name (Just ty) Nothing
|
2020-06-01 18:17:33 +04:00
|
|
|
|
|
|
|
instance UpdateOver ScopeM Mutable (Pascal a)
|
2020-06-09 15:56:11 +04:00
|
|
|
instance UpdateOver ScopeM Type (Pascal a)
|
2020-06-01 18:17:33 +04:00
|
|
|
instance UpdateOver ScopeM Variant (Pascal a)
|
2020-06-09 15:56:11 +04:00
|
|
|
instance UpdateOver ScopeM TField (Pascal a)
|
2020-06-01 18:17:33 +04:00
|
|
|
|
|
|
|
instance HasRange a => UpdateOver ScopeM Expr (Pascal a) where
|
2020-06-10 22:37:02 +04:00
|
|
|
before r = \case
|
|
|
|
Let {} -> enter r
|
|
|
|
Lambda {} -> enter r
|
2020-06-01 18:17:33 +04:00
|
|
|
ForLoop k _ _ _ -> do
|
2020-06-10 22:37:02 +04:00
|
|
|
enter r
|
2020-06-01 18:17:33 +04:00
|
|
|
def k Nothing Nothing
|
|
|
|
|
|
|
|
ForBox k mv _ _ _ -> do
|
2020-06-10 22:37:02 +04:00
|
|
|
enter r
|
2020-06-01 18:17:33 +04:00
|
|
|
def k Nothing Nothing
|
|
|
|
maybe skip (\v -> def v Nothing Nothing) mv
|
|
|
|
|
|
|
|
_ -> skip
|
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
after _ = \case
|
2020-06-01 18:17:33 +04:00
|
|
|
Let {} -> leave
|
|
|
|
Lambda {} -> leave
|
|
|
|
ForLoop {} -> leave
|
|
|
|
ForBox {} -> leave
|
|
|
|
_ -> skip
|
|
|
|
|
|
|
|
instance HasRange a => UpdateOver ScopeM Alt (Pascal a) where
|
2020-06-10 22:37:02 +04:00
|
|
|
before r _ = enter r
|
|
|
|
after _ _ = leave
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-09 15:56:11 +04:00
|
|
|
instance UpdateOver ScopeM LHS (Pascal a)
|
|
|
|
instance UpdateOver ScopeM MapBinding (Pascal a)
|
|
|
|
instance UpdateOver ScopeM Assignment (Pascal a)
|
2020-06-01 18:17:33 +04:00
|
|
|
instance UpdateOver ScopeM FieldAssignment (Pascal a)
|
2020-06-09 15:56:11 +04:00
|
|
|
instance UpdateOver ScopeM Constant (Pascal a)
|
2020-06-01 18:17:33 +04:00
|
|
|
|
|
|
|
instance HasRange a => UpdateOver ScopeM Pattern (Pascal a) where
|
2020-06-10 22:37:02 +04:00
|
|
|
before _ = \case
|
2020-06-01 18:17:33 +04:00
|
|
|
IsVar n -> def n Nothing Nothing
|
|
|
|
_ -> skip
|
|
|
|
|
|
|
|
instance UpdateOver ScopeM QualifiedName (Pascal a)
|
2020-06-09 15:56:11 +04:00
|
|
|
instance UpdateOver ScopeM Path (Pascal a)
|
2020-06-10 22:37:02 +04:00
|
|
|
instance UpdateOver ScopeM Name (Pascal a) where
|
|
|
|
before range (Name raw) = do
|
|
|
|
modify $ addRef raw range
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-09 15:56:11 +04:00
|
|
|
class HasEnv a where
|
|
|
|
getEnv :: a -> Env
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-09 15:56:11 +04:00
|
|
|
instance HasEnv Env where
|
|
|
|
getEnv = id
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-09 15:56:11 +04:00
|
|
|
instance Contains Env xs => HasEnv (Product xs) where
|
|
|
|
getEnv = getElem
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-07 23:51:44 +04:00
|
|
|
data Scope = Scope { unScope :: [Text] }
|
2020-06-01 18:17:33 +04:00
|
|
|
|
|
|
|
instance HasComments Scope where
|
2020-06-07 23:51:44 +04:00
|
|
|
getComments = unScope
|
2020-06-01 18:17:33 +04:00
|
|
|
|
2020-06-10 22:37:02 +04:00
|
|
|
-- pinEnv :: Product xs -> ScopeM (Product (Env : xs))
|
|
|
|
-- pinEnv xs = (`Cons` xs) <$> gets head
|
|
|
|
|
|
|
|
collectEnv :: Contains Range xs => Product xs -> ScopeM (Product (Scopes : xs))
|
|
|
|
collectEnv xs = do
|
|
|
|
gets \case
|
|
|
|
st : _ -> Cons st xs
|
|
|
|
[] -> Cons (mk (getRange xs) $ ScopeTree Map.empty []) xs
|
|
|
|
|
|
|
|
instance UpdateOver (State [Env]) ScopeTree Scopes where
|
|
|
|
before r (ScopeTree e _) = modify (e :)
|
|
|
|
after r _ = modify tail
|
|
|
|
|
|
|
|
distributeEnv :: ScopeTree Scopes -> State [Env] (ScopeTree Scopes)
|
|
|
|
distributeEnv (ScopeTree e' cs) = do
|
|
|
|
e <- gets (Map.unions . (e' :))
|
|
|
|
traceShowM ("distribute", e', e)
|
|
|
|
return $ ScopeTree e cs
|
|
|
|
|
|
|
|
pinEnv :: Contains Range xs => Scopes -> Product xs -> ScopeM (Product (Env : xs))
|
|
|
|
pinEnv scopes info = do
|
|
|
|
let (_, ScopeTree e _) = fromJust $ match =<< lookupTree (getElem info) scopes
|
|
|
|
return (Cons e info)
|
|
|
|
|
|
|
|
instance HasComments Range where
|
|
|
|
getComments _ = []
|
|
|
|
|
|
|
|
instance Pretty (Product xs) => HasComments (Product xs) where
|
|
|
|
getComments xs = if Text.null $ Text.strip x then [] else [x]
|
|
|
|
where
|
|
|
|
x = ppToText $ color 3 $ pp $ xs
|
|
|
|
|
|
|
|
ascribeEnv :: (Contains Range xs, Pretty (Product xs)) => Pascal (Product xs) -> Pascal (Product (Env : xs))
|
|
|
|
ascribeEnv tree =
|
|
|
|
let
|
|
|
|
Cons scopes _ = traceShowId $ infoOf $ evalScopeM $ traverseTree collectEnv tree
|
|
|
|
distributed = evalState (traverseOnly distributeEnv scopes) []
|
|
|
|
in
|
|
|
|
-- distributed
|
|
|
|
evalScopeM $ traverseTree (pinEnv distributed) tree
|