Add integers to AST. Find references in assignment statements.

This commit is contained in:
Frederik Hanghøj Iversen 2019-11-13 13:36:48 +01:00
parent bdc6dcda5b
commit fa3093e61c
7 changed files with 78 additions and 10 deletions

View file

@ -36,4 +36,22 @@ in the call tree.
} }
Write graphviz output. The above problem is sort of fixed by just writing DOT output as it
takes care of cycles.
# Instance methods
Instance methods are a common occurrence in Ruby code. However they
aren't really resolved in a nice way. E.g.:
def f
person.name
end
Will produce:
{"f":["name"]}
Which is misleading. What we could do it try and find out what
`person` is referring to and then "inline" it. For example. Assume
that we have `"person": ["Person.new"]` in our references then we
could add an entry for `f` like so: `"f": ["Person.name"]`.

View file

@ -35,6 +35,7 @@ dependencies:
- temporary - temporary
- text - text
- hashable - hashable
- scientific
default-extensions: default-extensions:
- ConstraintKinds - ConstraintKinds

View file

@ -20,16 +20,19 @@ module Data.Language.Ruby.AST
, Ivar(..) , Ivar(..)
, Atom(..) , Atom(..)
, Defs(..) , Defs(..)
, Self , Self(..)
, Nil , Nil(..)
, Cbase , Cbase(..)
, Int(..)
) where ) where
import Data.Aeson (parseJSON, Value(Null,Object,Number,Bool), withArray) import Data.Aeson (parseJSON, Value(Null,Object,Number,Bool), withArray)
import Frelude hiding (String) import Prelude (Bounded)
import Frelude hiding (String, Int)
import qualified Frelude import qualified Frelude
import qualified Data.Aeson.Types as Aeson import qualified Data.Aeson.Types as Aeson
import qualified Data.Vector as Vector import qualified Data.Vector as Vector
import qualified Data.Scientific as Scientific
kebabCase :: Frelude.String -> Frelude.String kebabCase :: Frelude.String -> Frelude.String
kebabCase = Aeson.camelTo2 '-' kebabCase = Aeson.camelTo2 '-'
@ -73,6 +76,7 @@ data Statement
| StmtIvar Ivar | StmtIvar Ivar
| StmtSelf Self | StmtSelf Self
| StmtNil Nil | StmtNil Nil
| StmtInt Int
| StmtCbase Cbase | StmtCbase Cbase
-- TODO Get rid of this -- TODO Get rid of this
| StmtAnything Anything | StmtAnything Anything
@ -101,6 +105,7 @@ instance ToJSON Statement where
"StmtIvar" -> "ivar" "StmtIvar" -> "ivar"
"StmtSelf" -> "self" "StmtSelf" -> "self"
"StmtNil" -> "nil" "StmtNil" -> "nil"
"StmtInt" -> "int"
"StmtCbase" -> "cbase" "StmtCbase" -> "cbase"
x -> x x -> x
@ -121,6 +126,7 @@ instance FromJSON Statement where
<|> (StmtIvar <$> parseJSON v) <|> (StmtIvar <$> parseJSON v)
<|> (StmtSelf <$> parseJSON v) <|> (StmtSelf <$> parseJSON v)
<|> (StmtNil <$> parseJSON v) <|> (StmtNil <$> parseJSON v)
<|> (StmtInt <$> parseJSON v)
<|> (StmtCbase <$> parseJSON v) <|> (StmtCbase <$> parseJSON v)
<|> (StmtAnything <$> parseJSON v) <|> (StmtAnything <$> parseJSON v)
@ -486,3 +492,29 @@ instance FromJSON Nil where
parseJSON = \case parseJSON = \case
Aeson.Null -> pure Nil Aeson.Null -> pure Nil
_ -> empty _ -> empty
newtype Int = Int Frelude.Int
deriving stock instance Show Int
deriving newtype instance Ord Int
deriving newtype instance Eq Int
deriving stock instance Generic Int
deriving newtype instance Enum Int
deriving newtype instance Num Int
deriving newtype instance Real Int
deriving newtype instance Integral Int
deriving newtype instance Bounded Int
instance ToJSON Int where
toEncoding = Aeson.genericToEncoding aesonOptions
instance FromJSON Int where
parseJSON = withArray "Int" $ \case
[Aeson.String "int", atom] -> parseIntegral atom
_ -> empty
parseIntegral :: forall f n . Alternative f => Bounded n => Integral n => Value -> f n
parseIntegral = \case
Aeson.Number s -> case Scientific.toBoundedInteger @n s of
Nothing -> empty
Just n -> pure $ fromIntegral n
_ -> empty

View file

@ -138,8 +138,8 @@ instance Semigroup Node where
(NodeModule n0, NodeModule n1) -> NodeModule $ n0 <> n1 (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)) -> NodeDef $ FQN (n0 <> n1) f (NodeModule n0, NodeDef (FQN n1 f)) -> NodeDef $ FQN (n0 <> n1) f
(NodeDef{}, NodeModule{}) -> error "Cannot append module to function context." -- TODO: Problematic: Is a Node really a semigroup with this representation?
-- (NodeDef{}, NodeDef{}) -> error "Cannot append function to function context." (NodeDef{}, NodeModule{}) -> c1
(NodeDef (FQN n0 _), NodeDef (FQN n1 a1)) -> NodeDef $ FQN (n0 <> n1) a1 (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
@ -250,6 +250,7 @@ instance References Ruby.Statement where
Ruby.StmtSelf s -> entries s Ruby.StmtSelf s -> entries s
Ruby.StmtCbase s -> entries s Ruby.StmtCbase s -> entries s
Ruby.StmtNil n -> entries n Ruby.StmtNil n -> entries n
Ruby.StmtInt n -> entries n
Ruby.StmtAnything a -> entries a Ruby.StmtAnything a -> entries a
instance References Ruby.Block where instance References Ruby.Block where
@ -268,7 +269,10 @@ instance References Ruby.Sym where
instance References Ruby.Str where instance References Ruby.Str where
entries _ = pure () entries _ = pure ()
instance References Ruby.Lvasgn where instance References Ruby.Lvasgn where
entries _ = pure () entries (Ruby.Lvasgn _ b) = entries b
-- We don't consider "local variables". We could do though and say
-- e.g. the local variable is a `Node` with the same "context" as the
-- caller.
instance References Ruby.Lvar where instance References Ruby.Lvar where
entries _ = pure () entries _ = pure ()
instance References Ruby.Ivar where instance References Ruby.Ivar where
@ -283,6 +287,8 @@ instance References Ruby.Array where
entries Ruby.Array{statements} = traverse_ entries statements entries Ruby.Array{statements} = traverse_ entries statements
instance References Ruby.Anything where instance References Ruby.Anything where
entries = const $ pure () entries = const $ pure ()
instance References Ruby.Int where
entries = const $ pure ()
instance References Ruby.Const where instance References Ruby.Const where
entries con = application $ NodeModule $ constToNamespace con entries con = application $ NodeModule $ constToNamespace con
@ -344,7 +350,10 @@ statementToNamespace = go mempty
Ruby.StmtSend{} -> acc Ruby.StmtSend{} -> acc
Ruby.StmtIvar{} -> acc Ruby.StmtIvar{} -> acc
Ruby.StmtLvar{} -> acc Ruby.StmtLvar{} -> acc
_ -> error "Can only build namespaces from sequences of `const` statements" Ruby.StmtArray{} -> acc
-- TODO: Catch-all. Is this what we want to do? See e.g. the
-- note about instance methods in the backlog.
_ -> acc
constToNamespace :: Ruby.Const -> Namespace constToNamespace :: Ruby.Const -> Namespace
constToNamespace Ruby.Const{context, atom} = statementToNamespace context <> [k] constToNamespace Ruby.Const{context, atom} = statementToNamespace context <> [k]

View file

@ -17,7 +17,7 @@ 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", "nested", "closest"] tests = go <$> ["mod", "simple", "nested", "closest", "assignment"]
where where
go :: String -> TestTree go :: String -> TestTree
go s = testCase s $ do go s = testCase s $ do

View file

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

3
test/tests/assignment.rb Normal file
View file

@ -0,0 +1,3 @@
A
b = A