2019-10-11 09:08:36 +00:00
|
|
|
{-# LANGUAGE DuplicateRecordFields, OverloadedLists, InstanceSigs #-}
|
2019-10-16 18:12:07 +00:00
|
|
|
-- {-# OPTIONS_GHC -Wall #-}
|
2019-10-03 14:13:34 +00:00
|
|
|
module Rubyhs.References
|
|
|
|
( References(entries)
|
|
|
|
, Entry(..)
|
|
|
|
, FQN(..)
|
|
|
|
, references
|
2019-10-11 09:08:36 +00:00
|
|
|
, Env(..)
|
2019-10-11 09:37:11 +00:00
|
|
|
, Result(..)
|
2019-10-11 12:03:01 +00:00
|
|
|
, Namespace(..)
|
2019-10-11 14:55:48 +00:00
|
|
|
, Context(..)
|
2019-10-16 18:12:07 +00:00
|
|
|
, graph
|
|
|
|
, prettyContext
|
|
|
|
, Node(..)
|
2019-10-03 14:13:34 +00:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Frelude
|
2019-10-11 12:03:01 +00:00
|
|
|
import Data.Language.Ruby hiding (Namespace)
|
|
|
|
import qualified Data.Language.Ruby as Ruby
|
2019-10-11 09:08:36 +00:00
|
|
|
import Data.Map (Map)
|
2019-10-03 14:13:34 +00:00
|
|
|
import qualified Data.Map as Map
|
|
|
|
import Data.Kind
|
|
|
|
import Control.Monad.State
|
|
|
|
import qualified Data.Aeson as Aeson
|
2019-10-11 12:03:01 +00:00
|
|
|
import Data.Set (Set)
|
|
|
|
import qualified Data.Set as Set
|
2019-10-15 18:46:28 +00:00
|
|
|
import qualified Data.Text as Text
|
|
|
|
import Data.Coerce
|
2019-10-16 18:12:07 +00:00
|
|
|
import Data.Graph (Graph, Vertex, Forest)
|
|
|
|
import qualified Data.Graph as Graph
|
|
|
|
|
|
|
|
span :: G node key -> Forest node
|
|
|
|
span (g, f, _) = fmap ((\(x, _, _) -> x) . f) <$> Graph.dff g
|
|
|
|
|
|
|
|
type G node key = (Graph, Vertex -> (node, key, [key]), key -> Maybe Vertex)
|
|
|
|
|
2019-10-17 18:08:25 +00:00
|
|
|
graph :: Begin -> Forest Node
|
2019-10-16 18:12:07 +00:00
|
|
|
graph b = span $ Graph.graphFromEdges $ go <$> toList ys
|
|
|
|
where
|
|
|
|
go :: (Node, Set Node) -> (Node, Text, [Text])
|
|
|
|
go (x, xs) = (x, prettyContext x, prettyContext <$> toList xs)
|
|
|
|
Result ys = references b
|
2019-10-03 14:13:34 +00:00
|
|
|
|
|
|
|
data Entry a = Entry
|
|
|
|
{ node :: a
|
|
|
|
, fqn :: FQN
|
|
|
|
}
|
|
|
|
|
2019-10-11 12:03:01 +00:00
|
|
|
newtype Namespace = Namespace [Text]
|
|
|
|
deriving stock instance Show Namespace
|
|
|
|
deriving stock instance Eq Namespace
|
|
|
|
deriving stock instance Ord Namespace
|
|
|
|
instance IsList Namespace where
|
|
|
|
type Item Namespace = Text
|
2019-10-15 19:50:19 +00:00
|
|
|
fromList = coerce
|
|
|
|
toList = coerce
|
2019-10-11 12:03:01 +00:00
|
|
|
deriving newtype instance Semigroup Namespace
|
|
|
|
deriving newtype instance Monoid Namespace
|
2019-10-15 18:46:28 +00:00
|
|
|
instance ToJSON Namespace where
|
2019-10-15 19:50:19 +00:00
|
|
|
toJSON = Aeson.String . showNamespace
|
|
|
|
|
|
|
|
showNamespace :: Namespace -> Text
|
|
|
|
showNamespace = Text.intercalate "::" . coerce
|
2019-10-11 12:03:01 +00:00
|
|
|
|
2019-10-11 09:37:11 +00:00
|
|
|
-- Names are in reverse order.
|
2019-10-11 12:03:01 +00:00
|
|
|
data FQN = FQN
|
|
|
|
{ namespace :: Namespace
|
|
|
|
, name :: Text
|
|
|
|
}
|
2019-10-03 14:13:34 +00:00
|
|
|
|
2019-10-15 18:46:28 +00:00
|
|
|
data Node = NodeFunction FQN | NodeModule Namespace
|
|
|
|
|
|
|
|
deriving stock instance Show Node
|
|
|
|
deriving stock instance Eq Node
|
|
|
|
deriving stock instance Ord Node
|
|
|
|
|
|
|
|
instance ToJSON Node where
|
|
|
|
toJSON = \case
|
|
|
|
NodeFunction q -> Aeson.toJSON q
|
|
|
|
NodeModule m -> Aeson.toJSON m
|
|
|
|
|
2019-10-11 09:08:36 +00:00
|
|
|
deriving stock instance Show FQN
|
2019-10-11 12:03:01 +00:00
|
|
|
deriving stock instance Eq FQN
|
|
|
|
deriving stock instance Ord FQN
|
|
|
|
instance Pretty Namespace where
|
2019-10-15 19:50:19 +00:00
|
|
|
pretty = pretty . showNamespace
|
2019-10-11 12:03:01 +00:00
|
|
|
|
|
|
|
instance Aeson.ToJSON FQN where
|
|
|
|
toJSON = Aeson.String . prettyFQN
|
2019-10-11 09:08:36 +00:00
|
|
|
instance Aeson.ToJSONKey FQN where
|
|
|
|
instance Pretty FQN where
|
2019-10-11 12:03:01 +00:00
|
|
|
pretty = pretty . prettyFQN
|
|
|
|
|
|
|
|
prettyFQN :: FQN -> Text
|
2019-10-15 18:46:28 +00:00
|
|
|
prettyFQN FQN{name, namespace} = case namespace of
|
2019-10-11 12:03:01 +00:00
|
|
|
[] -> name
|
2019-10-15 19:50:19 +00:00
|
|
|
_ -> convertString (showNamespace namespace) <> "." <> name
|
2019-10-03 14:13:34 +00:00
|
|
|
|
2019-10-15 18:46:28 +00:00
|
|
|
newtype Context = Context Node
|
2019-10-11 14:55:48 +00:00
|
|
|
|
|
|
|
deriving stock instance Eq Context
|
|
|
|
deriving stock instance Ord Context
|
2019-10-15 18:46:28 +00:00
|
|
|
deriving newtype instance ToJSON Context
|
2019-10-11 14:55:48 +00:00
|
|
|
|
2019-10-03 14:13:34 +00:00
|
|
|
class Monad m => MyMonad (m :: Type -> Type) where
|
2019-10-15 18:46:28 +00:00
|
|
|
declaration :: Node -> m ()
|
2019-10-16 20:43:19 +00:00
|
|
|
application :: Node -> m ()
|
2019-10-11 14:55:48 +00:00
|
|
|
getContext :: m Context
|
|
|
|
writeContext :: Context -> m ()
|
2019-10-03 14:13:34 +00:00
|
|
|
|
|
|
|
data Env = Env
|
2019-10-15 18:46:28 +00:00
|
|
|
{ declarations :: Set Node
|
|
|
|
, applications :: Map Node (Set Node)
|
2019-10-11 14:55:48 +00:00
|
|
|
, context :: Context
|
2019-10-03 14:13:34 +00:00
|
|
|
}
|
|
|
|
|
2019-10-16 18:12:07 +00:00
|
|
|
newtype Result = Result (Map Node (Set Node))
|
2019-10-11 09:37:11 +00:00
|
|
|
instance ToJSON Result where
|
2019-10-16 18:12:07 +00:00
|
|
|
toJSON (Result a) = Aeson.Object $ fromList $ go <$> toList a
|
2019-10-11 09:37:11 +00:00
|
|
|
where
|
2019-10-16 18:12:07 +00:00
|
|
|
go :: (Node, Set Node) -> (Text, Aeson.Value)
|
|
|
|
go (x, y) = (prettyContext x, Aeson.toJSON y)
|
2019-10-11 09:37:11 +00:00
|
|
|
|
2019-10-15 18:46:28 +00:00
|
|
|
prettyContext :: Node -> Text
|
2019-10-11 14:55:48 +00:00
|
|
|
prettyContext = \case
|
2019-10-15 18:46:28 +00:00
|
|
|
NodeFunction fun -> prettyFQN fun
|
2019-10-15 19:50:19 +00:00
|
|
|
NodeModule ns -> showNamespace ns
|
2019-10-03 14:13:34 +00:00
|
|
|
|
|
|
|
instance MyMonad (State Env) where
|
2019-10-11 12:03:01 +00:00
|
|
|
declaration q = modify go
|
2019-10-03 14:13:34 +00:00
|
|
|
where
|
2019-10-11 09:37:11 +00:00
|
|
|
go :: Env -> Env
|
2019-10-11 12:03:01 +00:00
|
|
|
go env@Env{declarations} = env { declarations = Set.insert q declarations }
|
2019-10-16 20:43:19 +00:00
|
|
|
application n = do
|
|
|
|
Context c <- getContext
|
|
|
|
let
|
|
|
|
go env@Env{applications}
|
|
|
|
= env { applications = Map.insertWith mappend c (Set.singleton n) applications }
|
|
|
|
modify go
|
2019-10-03 14:13:34 +00:00
|
|
|
getContext = gets context
|
|
|
|
writeContext q = modify go
|
|
|
|
where
|
|
|
|
go env = env { context = q }
|
|
|
|
|
2019-10-11 14:55:48 +00:00
|
|
|
updateContext :: MyMonad m => (Context -> Context) -> m ()
|
2019-10-03 14:13:34 +00:00
|
|
|
updateContext f = getContext >>= \c -> writeContext (f c)
|
|
|
|
|
|
|
|
locally :: MyMonad m => m a -> m a
|
|
|
|
locally act = do
|
|
|
|
old <- getContext
|
|
|
|
res <- act
|
|
|
|
writeContext old
|
|
|
|
pure res
|
|
|
|
|
|
|
|
appendToContext :: MyMonad m => Name -> m ()
|
|
|
|
appendToContext n = updateContext go
|
|
|
|
where
|
2019-10-15 18:46:28 +00:00
|
|
|
go (Context (NodeModule q)) = Context $ NodeModule $ name2ns n <> q
|
|
|
|
go (Context NodeFunction{}) = error "Cannot append module to context in function context"
|
2019-10-11 12:03:01 +00:00
|
|
|
|
|
|
|
name2ns :: Name -> Namespace
|
2019-10-11 14:26:15 +00:00
|
|
|
name2ns (Name o) = go o
|
|
|
|
where
|
|
|
|
go :: Aeson.Value -> Namespace
|
|
|
|
go = \case
|
|
|
|
Aeson.Array [Aeson.String "const", x, Aeson.String s] -> case x of
|
|
|
|
Aeson.Null -> [s]
|
|
|
|
_ -> go x <> [s]
|
|
|
|
_ -> error $ show o
|
2019-10-03 14:13:34 +00:00
|
|
|
|
|
|
|
class References a where
|
|
|
|
entries :: MyMonad m => a -> m ()
|
|
|
|
|
2019-10-17 18:08:25 +00:00
|
|
|
references :: Begin -> Result
|
2019-10-16 18:12:07 +00:00
|
|
|
references q = Result $ Map.unionWith mappend applications declarations'
|
2019-10-11 09:37:11 +00:00
|
|
|
where
|
2019-10-11 14:55:48 +00:00
|
|
|
Env{declarations,applications} = execState (entries @_ @(State Env) q) emptyEnv
|
2019-10-15 18:46:28 +00:00
|
|
|
emptyEnv = Env mempty mempty (Context $ NodeModule mempty)
|
2019-10-16 18:12:07 +00:00
|
|
|
declarations' = fromList $ (\x -> (x, mempty)) <$> toList declarations
|
2019-10-03 14:13:34 +00:00
|
|
|
|
2019-10-17 18:08:25 +00:00
|
|
|
instance References Begin where
|
|
|
|
entries :: forall m . MyMonad m => Begin -> m ()
|
|
|
|
entries (Begin defs) = traverse_ (locally . entries) defs
|
2019-10-03 14:13:34 +00:00
|
|
|
|
2019-10-14 18:25:09 +00:00
|
|
|
instance References Statement where
|
2019-10-03 14:13:34 +00:00
|
|
|
entries = \case
|
2019-10-15 18:46:28 +00:00
|
|
|
StmtModule m -> entries m
|
2019-10-14 18:25:09 +00:00
|
|
|
StmtFunction f -> entries f
|
2019-10-15 18:46:28 +00:00
|
|
|
StmtSend s -> entries s
|
|
|
|
StmtConst c -> entries c
|
2019-10-17 18:08:25 +00:00
|
|
|
StmtBlock b -> entries b
|
2019-10-16 20:12:30 +00:00
|
|
|
StmtCasgn c -> entries c
|
|
|
|
StmtArray a -> entries a
|
2019-10-17 18:08:25 +00:00
|
|
|
StmtSym s -> entries s
|
2019-10-16 20:12:30 +00:00
|
|
|
StmtAnything a -> entries a
|
|
|
|
|
2019-10-17 18:08:25 +00:00
|
|
|
instance References Ruby.Block where
|
|
|
|
entries Block{send,args,begin} = do
|
2019-10-16 20:35:15 +00:00
|
|
|
entries send
|
|
|
|
entries args
|
2019-10-17 18:08:25 +00:00
|
|
|
entries begin
|
2019-10-16 20:35:15 +00:00
|
|
|
instance References Ruby.RArgs where
|
2019-10-16 20:12:30 +00:00
|
|
|
entries = const $ pure ()
|
2019-10-16 21:23:40 +00:00
|
|
|
-- TODO: We have to make a "declaration" for the constant here as
|
|
|
|
-- well!
|
2019-10-16 20:12:30 +00:00
|
|
|
instance References Ruby.Casgn where
|
2019-10-16 20:35:15 +00:00
|
|
|
entries Casgn{name, statement} = entries statement
|
2019-10-17 18:08:25 +00:00
|
|
|
instance References Ruby.Sym where
|
|
|
|
entries _ = pure ()
|
2019-10-16 20:12:30 +00:00
|
|
|
instance References Ruby.RArray where
|
2019-10-16 20:35:15 +00:00
|
|
|
entries RArray{statements} = traverse_ entries statements
|
2019-10-16 20:12:30 +00:00
|
|
|
instance References Ruby.Anything where
|
|
|
|
entries = const $ pure ()
|
2019-10-03 14:13:34 +00:00
|
|
|
|
2019-10-15 18:46:28 +00:00
|
|
|
instance References Ruby.Namespace where
|
2019-10-16 20:43:19 +00:00
|
|
|
entries (Ruby.Namespace xs) = do
|
|
|
|
Context c <- getContext
|
|
|
|
let
|
|
|
|
ctxt = case c of
|
|
|
|
NodeFunction FQN{namespace} -> namespace
|
|
|
|
NodeModule namespace -> namespace
|
|
|
|
-- TODO Hacky:
|
|
|
|
ns = Namespace ((\(Name (Aeson.String t)) -> t) <$> xs)
|
|
|
|
-- TODO: Broken
|
|
|
|
-- application c (NodeModule $ ctxt `onTop` ns)
|
|
|
|
application (NodeModule $ ns)
|
2019-10-15 18:46:28 +00:00
|
|
|
|
2019-10-03 14:13:34 +00:00
|
|
|
instance References Module where
|
2019-10-17 18:08:25 +00:00
|
|
|
entries Module{name, begin} = do
|
2019-10-03 14:13:34 +00:00
|
|
|
appendToContext name
|
2019-10-15 18:46:28 +00:00
|
|
|
c <- getContext >>= \case
|
|
|
|
Context (NodeModule c) -> pure c
|
|
|
|
_ -> error "..."
|
|
|
|
declaration $ NodeModule c
|
2019-10-17 18:08:25 +00:00
|
|
|
entries begin
|
|
|
|
|
|
|
|
nameToNode :: MyMonad m => Name -> m Node
|
|
|
|
nameToNode name = do
|
|
|
|
namespace <- getContext >>= \case
|
|
|
|
Context (NodeModule c) -> pure c
|
|
|
|
Context NodeFunction{} -> error "Cannot have a function declaration in a function context"
|
|
|
|
pure $ NodeFunction $ qual namespace name
|
2019-10-03 14:13:34 +00:00
|
|
|
|
|
|
|
instance References Function where
|
2019-10-17 18:08:25 +00:00
|
|
|
entries Function{name, begin} = do
|
|
|
|
node <- nameToNode name
|
|
|
|
declaration node
|
2019-10-11 14:55:48 +00:00
|
|
|
locally $ do
|
2019-10-17 18:08:25 +00:00
|
|
|
writeContext (Context node)
|
|
|
|
entries begin
|
2019-10-03 14:13:34 +00:00
|
|
|
|
2019-10-11 12:03:01 +00:00
|
|
|
qual :: Namespace -> Name -> FQN
|
|
|
|
qual namespace (Name o) = case o of
|
|
|
|
Aeson.String name -> FQN { namespace , name }
|
|
|
|
_ -> error $ show o
|
|
|
|
|
2019-10-15 19:50:19 +00:00
|
|
|
|
|
|
|
onTop' :: Eq a => [a] -> [a] -> [a]
|
|
|
|
onTop' [] y = y
|
|
|
|
onTop' x [] = x
|
|
|
|
onTop' (x:xss) ys@(y:yss) = if
|
|
|
|
| x == y -> pure x <> yss
|
|
|
|
| otherwise -> pure x <> xss `onTop'` ys
|
|
|
|
|
|
|
|
onTop :: Namespace -> Namespace -> Namespace
|
|
|
|
onTop (Namespace xs) (Namespace ys) = Namespace $ reverse $ reverse xs `onTop'` reverse ys
|
|
|
|
|
2019-10-03 14:13:34 +00:00
|
|
|
instance References Send where
|
2019-10-11 12:03:01 +00:00
|
|
|
entries Send{namespace, name} = do
|
2019-10-16 18:12:07 +00:00
|
|
|
-- TODO: Broken
|
|
|
|
-- application c $ NodeFunction $ qual (ctxt `onTop` fromNS namespace) name
|
2019-10-16 20:43:19 +00:00
|
|
|
application $ NodeFunction $ qual (fromNS namespace) name
|
2019-10-11 12:03:01 +00:00
|
|
|
where
|
|
|
|
fromNS :: Ruby.Namespace -> Namespace
|
|
|
|
fromNS (Ruby.Namespace l) = Namespace $ go <$> l
|
|
|
|
go :: Name -> Text
|
|
|
|
go (Name o) = case o of
|
|
|
|
Aeson.String s -> s
|
|
|
|
_ -> error $ show o
|