Also handle constants

This commit is contained in:
Frederik Hanghøj Iversen 2019-10-15 20:46:28 +02:00
parent 9dd4f3ee01
commit 00f0e154c4
3 changed files with 62 additions and 26 deletions

View file

@ -33,6 +33,7 @@ dependencies:
- unordered-containers - unordered-containers
- optparse-applicative - optparse-applicative
- temporary - temporary
- text
default-extensions: default-extensions:
- ConstraintKinds - ConstraintKinds

View file

@ -49,6 +49,9 @@ data Statement
| StmtFunction Function | StmtFunction Function
| StmtSend Send | StmtSend Send
| StmtRBlock RBlock | StmtRBlock RBlock
-- TODO: We should also handle modules here. Otherwise we cannot
-- cover the case where a function references a module.
| StmtConst Namespace
| StmtAnything Anything | StmtAnything Anything
deriving stock instance Show Statement deriving stock instance Show Statement
@ -64,6 +67,7 @@ instance ToJSON Statement where
"StmtFunction" -> "function" "StmtFunction" -> "function"
"StmtSend" -> "send" "StmtSend" -> "send"
"StmtRBlock" -> "block" "StmtRBlock" -> "block"
"StmtConst" -> "const"
x -> x x -> x
instance FromJSON Statement where instance FromJSON Statement where
@ -72,6 +76,7 @@ instance FromJSON Statement where
<|> (StmtFunction <$> parseJSON v) <|> (StmtFunction <$> parseJSON v)
<|> (StmtSend <$> parseJSON v) <|> (StmtSend <$> parseJSON v)
<|> (StmtRBlock <$> parseJSON v) <|> (StmtRBlock <$> parseJSON v)
<|> (StmtConst <$> parseJSON v)
<|> (StmtAnything <$> parseJSON v) <|> (StmtAnything <$> parseJSON v)
newtype Anything = Anything Value newtype Anything = Anything Value

View file

@ -24,6 +24,8 @@ import Data.List
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Data.Text as Text
import Data.Coerce
data Entry a = Entry data Entry a = Entry
{ node :: a { node :: a
@ -40,6 +42,8 @@ instance IsList Namespace where
toList (Namespace l) = toList l toList (Namespace l) = toList l
deriving newtype instance Semigroup Namespace deriving newtype instance Semigroup Namespace
deriving newtype instance Monoid Namespace deriving newtype instance Monoid Namespace
instance ToJSON Namespace where
toJSON = Aeson.String . Text.intercalate "::" . coerce
-- Names are in reverse order. -- Names are in reverse order.
data FQN = FQN data FQN = FQN
@ -47,6 +51,17 @@ data FQN = FQN
, name :: Text , name :: Text
} }
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
deriving stock instance Show FQN deriving stock instance Show FQN
deriving stock instance Eq FQN deriving stock instance Eq FQN
deriving stock instance Ord FQN deriving stock instance Ord FQN
@ -60,47 +75,51 @@ instance Pretty FQN where
pretty = pretty . prettyFQN pretty = pretty . prettyFQN
prettyFQN :: FQN -> Text prettyFQN :: FQN -> Text
prettyFQN FQN{namespace, name} = case namespace of prettyFQN FQN{name, namespace} = case namespace of
[] -> name [] -> name
_ -> prettyNamespace namespace <> "." <> name _ -> prettyNamespace namespace <> "." <> name
prettyNamespace :: Namespace -> Text prettyNamespace :: Namespace -> Text
prettyNamespace = convertString . intercalate "::" . fmap convertString . reverse . toList prettyNamespace = convertString . intercalate "::" . fmap convertString . reverse . toList
data Context = ContextFun FQN | ContextMod Namespace newtype Context = Context Node
deriving stock instance Eq Context deriving stock instance Eq Context
deriving stock instance Ord Context deriving stock instance Ord Context
deriving newtype instance ToJSON Context
class Monad m => MyMonad (m :: Type -> Type) where class Monad m => MyMonad (m :: Type -> Type) where
declaration :: FQN -> m () declaration :: Node -> m ()
application :: Context -> FQN -> m () application :: Node -> Node -> m ()
getContext :: m Context getContext :: m Context
writeContext :: Context -> m () writeContext :: Context -> m ()
data Env = Env data Env = Env
{ declarations :: Set FQN { declarations :: Set Node
, applications :: Map Context (Set FQN) , applications :: Map Node (Set Node)
, context :: Context , context :: Context
} }
data Result = Result data Result = Result
{ declarations :: Set FQN { declarations :: Set Node
, applications :: Map Context (Set FQN) , applications :: Map Node (Set Node)
} }
instance ToJSON Result where instance ToJSON Result where
toJSON Result{declarations,applications} = Aeson.object toJSON Result{declarations,applications} = Aeson.object
[ "declarations" .= declarations [ "declarations" .= declarations
, "applications" .= f applications , "applications" .= f
] ]
where where
f x = fromList @(HashMap _ _) $ go <$> toList x f :: HashMap Text (Set Node)
go (x, y) = (prettyContext x, y) f = fromList @(HashMap _ _) $ go <$> toList applications
-- go :: (Node, Set Node) -> (Text, Set Node)
go :: (Node, Set Node) -> (Text, Set Node)
go (x, y) = (prettyContext x, y)
prettyContext :: Context -> Text prettyContext :: Node -> Text
prettyContext = \case prettyContext = \case
ContextFun q -> prettyFQN q NodeFunction fun -> prettyFQN fun
ContextMod ns -> prettyNamespace ns NodeModule ns -> prettyNamespace ns
instance MyMonad (State Env) where instance MyMonad (State Env) where
declaration q = modify go declaration q = modify go
@ -129,8 +148,8 @@ locally act = do
appendToContext :: MyMonad m => Name -> m () appendToContext :: MyMonad m => Name -> m ()
appendToContext n = updateContext go appendToContext n = updateContext go
where where
go (ContextMod q) = ContextMod $ name2ns n <> q go (Context (NodeModule q)) = Context $ NodeModule $ name2ns n <> q
go _ = error "Cannot append module to context in function context" go (Context NodeFunction{}) = error "Cannot append module to context in function context"
name2ns :: Name -> Namespace name2ns :: Name -> Namespace
name2ns (Name o) = go o name2ns (Name o) = go o
@ -149,7 +168,7 @@ references :: Block -> Result
references q = Result{declarations, applications} references q = Result{declarations, applications}
where where
Env{declarations,applications} = execState (entries @_ @(State Env) q) emptyEnv Env{declarations,applications} = execState (entries @_ @(State Env) q) emptyEnv
emptyEnv = Env mempty mempty (ContextMod mempty) emptyEnv = Env mempty mempty (Context $ NodeModule mempty)
instance References Block where instance References Block where
entries :: forall m . MyMonad m => Block -> m () entries :: forall m . MyMonad m => Block -> m ()
@ -157,26 +176,37 @@ instance References Block where
instance References Statement where instance References Statement where
entries = \case entries = \case
StmtModule m -> entries m StmtModule m -> entries m
StmtFunction f -> entries f StmtFunction f -> entries f
StmtSend s -> entries s StmtSend s -> entries s
StmtConst c -> entries c
-- TODO: -- TODO:
StmtRBlock{} -> pure () StmtRBlock{} -> pure ()
StmtAnything{} -> pure () StmtAnything{} -> pure ()
instance References Ruby.Namespace where
entries = \case
Ruby.Namespace xs -> do
Context c <- getContext
application c (NodeModule (Namespace ((\(Name (Aeson.String t)) -> t) <$> xs)))
instance References Module where instance References Module where
entries Module{name, block} = do entries Module{name, block} = do
appendToContext name appendToContext name
c <- getContext >>= \case
Context (NodeModule c) -> pure c
_ -> error "..."
declaration $ NodeModule c
entries block entries block
instance References Function where instance References Function where
entries Function{name, block} = do entries Function{name, block} = do
namespace <- getContext >>= \case namespace <- getContext >>= \case
ContextMod c -> pure c Context (NodeModule c) -> pure c
_ -> error "Cannot have a function declaration in a function context" Context NodeFunction{} -> error "Cannot have a function declaration in a function context"
declaration $ qual namespace name declaration $ NodeFunction $ qual namespace name
locally $ do locally $ do
writeContext (ContextFun $ qual namespace name) writeContext (Context $ NodeFunction $ qual namespace name)
entries block entries block
qual :: Namespace -> Name -> FQN qual :: Namespace -> Name -> FQN
@ -186,8 +216,8 @@ qual namespace (Name o) = case o of
instance References Send where instance References Send where
entries Send{namespace, name} = do entries Send{namespace, name} = do
c <- getContext Context c <- getContext
application c $ qual (fromNS namespace) name application c $ NodeFunction $ qual (fromNS namespace) name
where where
fromNS :: Ruby.Namespace -> Namespace fromNS :: Ruby.Namespace -> Namespace
fromNS (Ruby.Namespace l) = Namespace $ go <$> l fromNS (Ruby.Namespace l) = Namespace $ go <$> l