Add integers to AST. Find references in assignment statements.
This commit is contained in:
parent
bdc6dcda5b
commit
fa3093e61c
20
BACKLOG.md
20
BACKLOG.md
|
@ -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"]`.
|
||||||
|
|
|
@ -35,6 +35,7 @@ dependencies:
|
||||||
- temporary
|
- temporary
|
||||||
- text
|
- text
|
||||||
- hashable
|
- hashable
|
||||||
|
- scientific
|
||||||
|
|
||||||
default-extensions:
|
default-extensions:
|
||||||
- ConstraintKinds
|
- ConstraintKinds
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
5
test/tests/assignment.json
Normal file
5
test/tests/assignment.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"": [
|
||||||
|
"A"
|
||||||
|
]
|
||||||
|
}
|
3
test/tests/assignment.rb
Normal file
3
test/tests/assignment.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
A
|
||||||
|
|
||||||
|
b = A
|
Loading…
Reference in a new issue