Add some tests

Do not reverse namespaces.
This commit is contained in:
Frederik Hanghøj Iversen 2019-11-13 11:50:37 +01:00 committed by Frederik Hanghøj Iversen
parent 0c1e79f1fc
commit bdc6dcda5b
8 changed files with 118 additions and 31 deletions

View file

@ -12,6 +12,7 @@ module Rubyhs.References
, Node(..) , Node(..)
) where ) where
import qualified Prelude
import Frelude import Frelude
import qualified Data.Language.Ruby as Ruby import qualified Data.Language.Ruby as Ruby
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
@ -56,7 +57,7 @@ deriving newtype instance Semigroup Namespace
deriving newtype instance Monoid Namespace deriving newtype instance Monoid Namespace
instance FromJSON Namespace where instance FromJSON Namespace where
parseJSON = Aeson.withText "Namespace" parseJSON = Aeson.withText "Namespace"
$ \t -> Namespace <$> (traverse pure $ reverse $ Text.splitOn "::" t) $ \t -> Namespace <$> (traverse pure $ Text.splitOn "::" t)
instance ToJSON Namespace where instance ToJSON Namespace where
toJSON = Aeson.String . showNamespace toJSON = Aeson.String . showNamespace
@ -120,7 +121,7 @@ prettyContext = \case
NodeModule ns -> showNamespace ns NodeModule ns -> showNamespace ns
showNamespace :: Namespace -> Text showNamespace :: Namespace -> Text
showNamespace = Text.intercalate "::" . reverse . coerce showNamespace = Text.intercalate "::" . coerce
newtype Context = Context Node newtype Context = Context Node
@ -130,13 +131,16 @@ deriving newtype instance ToJSON Context
-- HACK: Not really a semigroup as is evident from the implementation. -- HACK: Not really a semigroup as is evident from the implementation.
instance Semigroup Context where instance Semigroup Context where
-- I think that the order must be swapped because they modules are in reverse order. -- I think that the order must be swapped because they modules are in reverse order.
Context c0 <> Context c1 = case traceShowId (c0, c1) of Context c0 <> Context c1 = Context $ c0 <> c1
instance Semigroup Node where
c0 <> c1 = case (c0, c1) of
-- (NodeModule n0, NodeModule n1) -> Context $ NodeModule $ n0 <> n1 -- (NodeModule n0, NodeModule n1) -> Context $ NodeModule $ n0 <> n1
(NodeModule n0, NodeModule n1) -> Context $ NodeModule $ n1 <> n0 (NodeModule n0, NodeModule n1) -> NodeModule $ n0 <> n1
-- (NodeModule n0, NodeDef (FQN n1 f)) -> Context $ NodeDef $ FQN (n0 <> n1) f -- (NodeModule n0, NodeDef (FQN n1 f)) -> Context $ NodeDef $ FQN (n0 <> n1) f
(NodeModule n0, NodeDef (FQN n1 f)) -> Context $ NodeDef $ FQN (n1 <> n0) f (NodeModule n0, NodeDef (FQN n1 f)) -> NodeDef $ FQN (n0 <> n1) f
(NodeDef{}, NodeModule{}) -> error "Cannot append module to function context." (NodeDef{}, NodeModule{}) -> error "Cannot append module to function context."
(NodeDef{}, NodeDef{}) -> error "Cannot append function to function context." -- (NodeDef{}, NodeDef{}) -> error "Cannot append function to function context."
(NodeDef (FQN n0 _), NodeDef (FQN n1 a1)) -> NodeDef $ FQN (n0 <> n1) a1
class Monad m => MyMonad (m :: Type -> Type) where class Monad m => MyMonad (m :: Type -> Type) where
declaration :: Node -> m () declaration :: Node -> m ()
@ -166,17 +170,20 @@ instance MyMonad (State Env) where
go env@Env{declarations} = env { declarations = Set.insert q declarations } go env@Env{declarations} = env { declarations = Set.insert q declarations }
application n = do application n = do
Context c <- getContext Context c <- getContext
decls <- gets declarations
n' <- findClosest decls n
let let
n' = case n of -- n' = findCloses n
-- TODO: Maybe check if there is `a` is a function in the -- case n of
-- current closure. -- -- TODO: Maybe check if there is `a` is a function in the
(NodeDef (FQN [] a)) -> NodeDef $ FQN (nodeNs c) a -- -- current closure.
-- Look for the closest enclosing scope that has a reference -- (NodeDef (FQN [] a)) -> NodeDef $ FQN (nodeNs c) a
-- to `c`. For now we'll just guess that it is a fully -- -- Look for the closest enclosing scope that has a reference
-- qualified reference. -- -- to `c`. For now we'll just guess that it is a fully
NodeDef{} -> n -- -- qualified reference.
-- Ditto as above. -- NodeDef{} -> n
NodeModule{} -> n -- -- Ditto as above.
-- NodeModule{} -> n
go env@Env{applications} go env@Env{applications}
= env { applications = HashMap.insertWith mappend c (Set.singleton n') applications } = env { applications = HashMap.insertWith mappend c (Set.singleton n') applications }
modify go modify go
@ -185,10 +192,24 @@ instance MyMonad (State Env) where
where where
go env = env { Rubyhs.References.context = q } go env = env { Rubyhs.References.context = q }
nodeNs :: Node -> Namespace findClosest :: MyMonad m => Set Node -> Node -> m Node
nodeNs = \case findClosest decls n = do
(NodeDef (FQN a _)) -> a Context c <- getContext
NodeModule n -> n pure $ go $ c
where
go :: Node -> Node
go c = case Set.member (c <> n) decls of
True -> c <> n
False -> case c of
NodeModule [] -> case n of -- Last resort
NodeDef (FQN _ a) -> NodeDef (FQN mempty a)
x -> x
_ -> go $ drop c
drop :: Node -> Node
drop = \case
(NodeDef (FQN a _)) -> NodeModule a
(NodeModule (Namespace [])) -> error "__IMPOSSIBLE__"
(NodeModule (Namespace xs)) -> NodeModule $ Namespace $ reverse $ Prelude.tail $ reverse xs
locally :: MyMonad m => m a -> m a locally :: MyMonad m => m a -> m a
locally act = do locally act = do
@ -271,7 +292,7 @@ updateContext f = getContext >>= \c -> writeContext (f c)
instance References Ruby.Module where instance References Ruby.Module where
entries Ruby.Module{name, begin} = do entries Ruby.Module{name, begin} = do
updateContext $ (<>) $ Context $ NodeModule $ constToNamespace name updateContext $ (<> (Context $ NodeModule $ constToNamespace name))
c <- getContext >>= \case c <- getContext >>= \case
Context (NodeModule c) -> pure c Context (NodeModule c) -> pure c
_ -> error "..." _ -> error "..."
@ -280,12 +301,12 @@ instance References Ruby.Module where
atomToNode :: MyMonad m => Ruby.Atom -> m Node atomToNode :: MyMonad m => Ruby.Atom -> m Node
atomToNode (Ruby.Atom name) = do atomToNode (Ruby.Atom name) = do
namespace <- getContext >>= \case namespace <- getContext >>= pure . \case
Context (NodeModule c) -> pure c Context (NodeModule c) -> c
-- We could allow this and just say that the function defined in -- We could allow this and just say that the function defined in
-- another function sits in the same context as the surrounding -- another function sits in the same context as the surrounding
-- function. -- function.
Context NodeDef{} -> error "Cannot have a function declaration in a function context" Context (NodeDef (FQN ns _)) -> ns
pure $ NodeDef $ FQN namespace name pure $ NodeDef $ FQN namespace name
instance References Ruby.Def where instance References Ruby.Def where

View file

@ -1,8 +1,7 @@
{-# LANGUAGE BangPatterns #-} {-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -Wall #-}
module Main (main) where module Main (main) where
import Data.List
import Data.Ord
import Frelude import Frelude
import Test.Tasty import Test.Tasty
import Test.Tasty.HUnit import Test.Tasty.HUnit
@ -10,19 +9,15 @@ import qualified Data.Language.Ruby as Ruby
import qualified Rubyhs.References as Ruby import qualified Rubyhs.References as Ruby
import qualified Data.Aeson as Aeson import qualified Data.Aeson as Aeson
import System.FilePath import System.FilePath
import Data.Map (Map)
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.Set (Set) import Data.Set (Set)
import System.Directory (withCurrentDirectory) import System.Directory (withCurrentDirectory)
import Debug.Trace
import System.IO
import qualified Data.ByteString.Lazy.Char8 as ByteString
main :: IO () main :: IO ()
main = withCurrentDirectory "test" $ defaultMain $ testGroup "Unit tests" tests main = withCurrentDirectory "test" $ defaultMain $ testGroup "Unit tests" tests
tests :: [TestTree] tests :: [TestTree]
tests = go <$> ["mod", "simple"] tests = go <$> ["mod", "simple", "nested", "closest"]
where where
go :: String -> TestTree go :: String -> TestTree
go s = testCase s $ do go s = testCase s $ do

13
test/tests/closest.json Normal file
View file

@ -0,0 +1,13 @@
{
"": [
"puts",
"A::B1.b1"
],
"A": [],
"A::B0": [],
"A::B0.b0": [],
"A::B1.b1": [
"A::B0.b0"
],
"A::B1": []
}

14
test/tests/closest.rb Normal file
View file

@ -0,0 +1,14 @@
module A
module B0
def self.b0
:b0
end
end
module B1
def self.b1
B0.b0
end
end
end
puts(A::B1.b1)

12
test/tests/deeper.json Normal file
View file

@ -0,0 +1,12 @@
{
"A": [],
"A::B": [
"puts",
"A::B.a",
"A::B::A::B.a"
],
"A::B.a": [],
"A::B::A": [],
"A::B::A::B": [],
"A::B::A::B.a": []
}

21
test/tests/deeper.rb Normal file
View file

@ -0,0 +1,21 @@
module A
module B
def self.a
:shallow
end
puts(A::B.a)
module A
module B
def self.a
:deep
end
end
end
# Had The inner A::B not been declared this would refer to the one
# that returns :shallow.
puts(A::B.a)
end
end

5
test/tests/nested.json Normal file
View file

@ -0,0 +1,5 @@
{
"A": [],
"A::B": [],
"A::B.b": []
}

6
test/tests/nested.rb Normal file
View file

@ -0,0 +1,6 @@
module A
module B
def self.b
end
end
end