1
1
mirror of https://github.com/github/semantic.git synced 2024-12-29 18:06:14 +03:00

Merge branch 'master' into imports,-graphed

This commit is contained in:
Rob Rix 2018-03-24 00:35:50 -04:00
commit 2e228604cf
64 changed files with 822 additions and 398 deletions

9
.gitmodules vendored
View File

@ -13,18 +13,9 @@
[submodule "vendor/effects"]
path = vendor/effects
url = https://github.com/joshvera/effects.git
[submodule "languages/c/vendor/tree-sitter-c"]
path = languages/c/vendor/tree-sitter-c
url = https://github.com/tree-sitter/tree-sitter-c.git
[submodule "languages/javascript/vendor/tree-sitter-javascript"]
path = languages/javascript/vendor/tree-sitter-javascript
url = https://github.com/tree-sitter/tree-sitter-javascript.git
[submodule "vendor/haskell-tree-sitter"]
path = vendor/haskell-tree-sitter
url = https://github.com/tree-sitter/haskell-tree-sitter.git
[submodule "vendor/freer-cofreer"]
path = vendor/freer-cofreer
url = https://github.com/robrix/freer-cofreer.git
[submodule "vendor/ghc-mod"]
path = vendor/ghc-mod
url = https://github.com/joshvera/ghc-mod

View File

@ -225,6 +225,8 @@ language_extensions:
- FlexibleContexts
- FlexibleInstances
- MultiParamTypeClasses
- StandaloneDeriving
- DataKinds
- OverloadedStrings
- RecordWildCards
- StrictData

View File

@ -74,6 +74,7 @@ library
, Data.Patch
, Data.Range
, Data.Record
, Data.Semigroup.App
, Data.Source
, Data.Span
, Data.SplitDiff
@ -183,14 +184,17 @@ library
, tree-sitter-ruby
, tree-sitter-typescript
default-language: Haskell2010
default-extensions: DeriveFoldable
default-extensions: DataKinds
, DeriveFoldable
, DeriveFunctor
, DeriveGeneric
, DeriveTraversable
, FlexibleContexts
, FlexibleInstances
, MultiParamTypeClasses
, OverloadedStrings
, RecordWildCards
, StandaloneDeriving
, StrictData
ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -fno-warn-name-shadowing -O -j
ghc-prof-options: -fprof-auto
@ -212,6 +216,7 @@ test-suite test
main-is: Spec.hs
other-modules: Assigning.Assignment.Spec
, Analysis.Go.Spec
, Analysis.PHP.Spec
, Analysis.Python.Spec
, Analysis.Ruby.Spec
, Analysis.TypeScript.Spec
@ -258,7 +263,15 @@ test-suite test
, these
ghc-options: -threaded -rtsopts -with-rtsopts=-N -j
default-language: Haskell2010
default-extensions: DeriveFunctor, DeriveGeneric, FlexibleContexts, FlexibleInstances, OverloadedStrings, RecordWildCards
default-extensions: DataKinds
, DeriveFunctor
, DeriveGeneric
, FlexibleContexts
, FlexibleInstances
, MultiParamTypeClasses
, OverloadedStrings
, RecordWildCards
, StandaloneDeriving
test-suite doctests
type: exitcode-stdio-1.0

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, GeneralizedNewtypeDeriving, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Analysis.Abstract.Caching
( type Caching
) where
@ -14,7 +14,7 @@ import Prologue
-- | The effects necessary for caching analyses.
type CachingEffects term value effects
= Fresh -- For 'MonadFresh'.
': NonDetEff -- For 'Alternative' and 'MonadNonDet'.
': NonDet -- For 'Alternative' and 'MonadNonDet'.
': Reader (CacheFor term value) -- The in-cache used as an oracle while converging on a result.
': State (CacheFor term value) -- The out-cache used to record results in each iteration of convergence.
': effects

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, GeneralizedNewtypeDeriving, MultiParamTypeClasses, StandaloneDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Analysis.Abstract.Collecting
( type Collecting
) where

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, GeneralizedNewtypeDeriving, MultiParamTypeClasses, StandaloneDeriving, TypeFamilies, TypeOperators #-}
{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies, TypeOperators #-}
module Analysis.Abstract.Dead
( type DeadCode
) where

View File

@ -1,22 +1,71 @@
{-# LANGUAGE DataKinds, GeneralizedNewtypeDeriving, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE DataKinds, GeneralizedNewtypeDeriving, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeApplications, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Analysis.Abstract.Evaluating
( type Evaluating
, findValue
, findEnv
, findHeap
, require
, load
) where
import Control.Abstract.Evaluator
import Control.Monad.Effect
import Control.Monad.Effect.Fail
import Control.Monad.Effect.Reader
import Control.Monad.Effect.State
import Control.Monad.Effect.Resumable
import Data.Abstract.Configuration
import qualified Data.Abstract.Environment as Env
import Data.Abstract.Environment (Environment)
import Data.Abstract.Heap (Heap(..))
import qualified Data.Abstract.Exports as Export
import Data.Abstract.Exports (Exports)
import Data.Abstract.Evaluatable
import Data.Abstract.Module
import Data.Abstract.ModuleTable
import Data.Abstract.Value
import qualified Data.IntMap as IntMap
import Prelude hiding (fail)
import Prologue
import Prologue hiding (throwError)
import qualified Data.ByteString.Char8 as BC
import qualified Data.Map.Monoidal as Monoidal
-- | Require/import another module by name and return it's environment and value.
--
-- Looks up the term's name in the cache of evaluated modules first, returns if found, otherwise loads/evaluates the module.
require :: (MonadAnalysis term value m, MonadValue value m)
=> ModuleName
-> m (EnvironmentFor value, value)
require name = getModuleTable >>= maybe (load name) pure . moduleTableLookup name
-- | Load another module by name and return it's environment and value.
--
-- Always loads/evaluates.
load :: (MonadAnalysis term value m, MonadValue value m)
=> ModuleName
-> m (EnvironmentFor value, value)
load name = askModuleTable >>= maybe notFound evalAndCache . moduleTableLookup name
where
notFound = fail ("cannot load module: " <> show name)
evalAndCache :: (MonadAnalysis term value m, MonadValue value m) => [Module term] -> m (EnvironmentFor value, value)
evalAndCache [] = (,) <$> pure mempty <*> unit
evalAndCache [x] = evalAndCache' x
evalAndCache (x:xs) = do
(env, _) <- evalAndCache' x
(env', v') <- evalAndCache xs
pure (env <> env', v')
evalAndCache' :: (MonadAnalysis term value m) => Module term -> m (EnvironmentFor value, value)
evalAndCache' x = do
v <- evaluateModule x
env <- filterEnv <$> getExports <*> getEnv
modifyModuleTable (moduleTableInsert name (env, v))
pure (env, v)
-- TODO: If the set of exports is empty because no exports have been
-- defined, do we export all terms, or no terms? This behavior varies across
-- languages. We need better semantics rather than doing it ad-hoc.
filterEnv :: Exports l a -> Environment l a -> Environment l a
filterEnv ports env
| Export.null ports = env
| otherwise = Export.toEnvironment ports <> Env.overwrite (Export.aliases ports) env
-- | An analysis evaluating @term@s to @value@s with a list of @effects@ using 'Evaluatable', and producing incremental results of type @a@.
newtype Evaluating term value effects a = Evaluating (Eff effects a)
@ -24,21 +73,41 @@ newtype Evaluating term value effects a = Evaluating (Eff effects a)
deriving instance Member Fail effects => MonadFail (Evaluating term value effects)
deriving instance Member Fresh effects => MonadFresh (Evaluating term value effects)
deriving instance Member NonDetEff effects => Alternative (Evaluating term value effects)
deriving instance Member NonDetEff effects => MonadNonDet (Evaluating term value effects)
deriving instance Member NonDet effects => Alternative (Evaluating term value effects)
deriving instance Member NonDet effects => MonadNonDet (Evaluating term value effects)
-- | Effects necessary for evaluating (whether concrete or abstract).
type EvaluatingEffects term value
= '[ Fail -- Failure with an error message
= '[ Resumable Prelude.String value
, Fail -- Failure with an error message
, Reader [Module term] -- The stack of currently-evaluating modules.
, State (EnvironmentFor value) -- Environments (both local and global)
, State (HeapFor value) -- The heap
, Reader (ModuleTable [Module term]) -- Cache of unevaluated modules
, State (ModuleTable (EnvironmentFor value)) -- Cache of evaluated modules
, Reader (ModuleTable [Module term]) -- Cache of unevaluated modules
, State (ModuleTable (EnvironmentFor value, value)) -- Cache of evaluated modules
, State (ExportsFor value) -- Exports (used to filter environments when they are imported)
, State (IntMap.IntMap term) -- For jumps
]
-- | Find the value in the 'Final' result of running.
findValue :: forall value term effects . (effects ~ RequiredEffects term value (Evaluating term value effects))
=> Final effects value -> Either Prelude.String (Either Prelude.String value)
findValue (((((v, _), _), _), _), _) = v
-- | Find the 'Environment' in the 'Final' result of running.
findEnv :: forall value term effects . (effects ~ RequiredEffects term value (Evaluating term value effects))
=> Final effects value -> EnvironmentFor value
findEnv (((((_, env), _), _), _), _) = env
-- | Find the 'Heap' in the 'Final' result of running.
findHeap :: forall value term effects . (effects ~ RequiredEffects term value (Evaluating term value effects))
=> Final effects value -> Monoidal.Map (LocationFor value) (CellFor value)
findHeap (((((_, _), Heap heap), _), _), _) = heap
instance Members '[Resumable Prelude.String value] effects => MonadThrow Prelude.String value (Evaluating term value effects) where
throwException = raise . throwError
instance Members '[Fail, State (IntMap.IntMap term)] effects => MonadControl term (Evaluating term value effects) where
label term = do
m <- raise get
@ -66,7 +135,7 @@ instance Member (State (HeapFor value)) effects => MonadHeap value (Evaluating t
getHeap = raise get
putHeap = raise . put
instance Members '[Reader (ModuleTable [Module term]), State (ModuleTable (EnvironmentFor value))] effects => MonadModuleTable term value (Evaluating term value effects) where
instance Members '[Reader (ModuleTable [Module term]), State (ModuleTable (EnvironmentFor value, value))] effects => MonadModuleTable term value (Evaluating term value effects) where
getModuleTable = raise get
putModuleTable = raise . put
@ -89,7 +158,7 @@ instance ( Evaluatable (Base term)
=> MonadAnalysis term value (Evaluating term value effects) where
type RequiredEffects term value (Evaluating term value effects) = EvaluatingEffects term value
analyzeTerm = eval
analyzeTerm term = resumeException @value (eval term) (\yield exc -> string (BC.pack exc) >>= yield)
analyzeModule m = pushModule (subterm <$> m) (subtermValue (moduleBody m))

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, GeneralizedNewtypeDeriving, MultiParamTypeClasses, StandaloneDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Analysis.Abstract.Tracing
( type Tracing
) where

View File

@ -10,7 +10,6 @@ import qualified Algebra.Graph as G
import Algebra.Graph.Class
import Algebra.Graph.Export.Dot
import Data.Abstract.FreeVariables
import Data.Set (member)
import qualified Data.Syntax as Syntax
import qualified Data.Syntax.Declaration as Declaration
import Data.Term
@ -21,7 +20,7 @@ newtype CallGraph = CallGraph { unCallGraph :: G.Graph Name }
deriving (Eq, Graph, Show)
-- | Build the 'CallGraph' for a 'Term' recursively.
buildCallGraph :: (CallGraphAlgebra syntax, FreeVariables1 syntax, Functor syntax) => Term syntax ann -> Set Name -> CallGraph
buildCallGraph :: (CallGraphAlgebra syntax, FreeVariables1 syntax, Functor syntax) => Term syntax ann -> [Name] -> CallGraph
buildCallGraph = foldSubterms callGraphAlgebra
@ -35,7 +34,7 @@ renderCallGraph = export (defaultStyle friendlyName) . unCallGraph
-- This typeclass employs the Advanced Overlap techniques designed by Oleg Kiselyov & Simon Peyton Jones: https://wiki.haskell.org/GHC/AdvancedOverlap.
class CallGraphAlgebra syntax where
-- | A 'SubtermAlgebra' computing the 'CallGraph' for a piece of @syntax@.
callGraphAlgebra :: FreeVariables term => syntax (Subterm term (Set Name -> CallGraph)) -> Set Name -> CallGraph
callGraphAlgebra :: FreeVariables term => syntax (Subterm term ([Name] -> CallGraph)) -> [Name] -> CallGraph
instance (CallGraphAlgebraStrategy syntax ~ strategy, CallGraphAlgebraWithStrategy strategy syntax) => CallGraphAlgebra syntax where
callGraphAlgebra = callGraphAlgebraWithStrategy (Proxy :: Proxy strategy)
@ -43,7 +42,7 @@ instance (CallGraphAlgebraStrategy syntax ~ strategy, CallGraphAlgebraWithStrate
-- | Types whose contribution to a 'CallGraph' is customized. If an instances definition is not being used, ensure that the type is mapped to 'Custom' in the 'CallGraphAlgebraStrategy'.
class CustomCallGraphAlgebra syntax where
customCallGraphAlgebra :: FreeVariables term => syntax (Subterm term (Set Name -> CallGraph)) -> Set Name -> CallGraph
customCallGraphAlgebra :: FreeVariables term => syntax (Subterm term ([Name] -> CallGraph)) -> [Name] -> CallGraph
-- | 'Declaration.Function's produce a vertex for their name, with edges to any free variables in their body.
instance CustomCallGraphAlgebra Declaration.Function where
@ -56,8 +55,8 @@ instance CustomCallGraphAlgebra Declaration.Method where
-- | 'Syntax.Identifier's produce a vertex iff its unbound in the 'Set'.
instance CustomCallGraphAlgebra Syntax.Identifier where
customCallGraphAlgebra (Syntax.Identifier name) bound
| name `member` bound = empty
| otherwise = vertex name
| name `elem` bound = empty
| otherwise = vertex name
instance Apply CallGraphAlgebra syntaxes => CustomCallGraphAlgebra (Union syntaxes) where
customCallGraphAlgebra = Prologue.apply (Proxy :: Proxy CallGraphAlgebra) callGraphAlgebra
@ -68,7 +67,7 @@ instance CallGraphAlgebra syntax => CustomCallGraphAlgebra (TermF syntax a) wher
-- | The mechanism selecting 'Default'/'Custom' implementations for 'callGraphAlgebra' depending on the @syntax@ type.
class CallGraphAlgebraWithStrategy (strategy :: Strategy) syntax where
callGraphAlgebraWithStrategy :: FreeVariables term => proxy strategy -> syntax (Subterm term (Set Name -> CallGraph)) -> Set Name -> CallGraph
callGraphAlgebraWithStrategy :: FreeVariables term => proxy strategy -> syntax (Subterm term ([Name] -> CallGraph)) -> [Name] -> CallGraph
-- | The 'Default' definition of 'callGraphAlgebra' combines all of the 'CallGraph's within the @syntax@ 'Monoid'ally.
instance Foldable syntax => CallGraphAlgebraWithStrategy 'Default syntax where

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Analysis.ConstructorName
( ConstructorName(..)
, ConstructorLabel(..)

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, TypeOperators, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
{-# LANGUAGE TypeOperators, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
module Analysis.Declaration
( Declaration(..)
, HasDeclaration

View File

@ -1,17 +1,17 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Analysis.IdentifierName
( IdentifierName(..)
, IdentifierLabel(..)
, identifierLabel
) where
import Data.Abstract.FreeVariables
import Data.Aeson
import Data.JSON.Fields
import Data.Term
import Data.Text.Encoding (decodeUtf8)
import Prologue
import Data.Abstract.FreeVariables
import Data.Aeson
import Data.JSON.Fields
import qualified Data.Syntax
import Data.Term
import Data.Text.Encoding (decodeUtf8)
import Prologue
-- | Compute a 'IdentifierLabel' label for a 'Term'.
identifierLabel :: IdentifierName syntax => TermF syntax a b -> Maybe IdentifierLabel

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables, TypeFamilies, UndecidableInstances #-}
module Analysis.ModuleDef
( ModuleDef(..)
, HasModuleDef

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, TypeFamilies #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-} -- For runAnalysis
module Control.Abstract.Analysis
( MonadAnalysis(..)
@ -6,8 +6,6 @@ module Control.Abstract.Analysis
, evaluateModule
, withModules
, evaluateModules
, require
, load
, liftAnalyze
, runAnalysis
, module X
@ -23,13 +21,8 @@ import qualified Control.Monad.Effect as Effect
import Control.Monad.Effect.Fail as X
import Control.Monad.Effect.Reader as X
import Control.Monad.Effect.State as X
import Data.Abstract.Environment (Environment)
import qualified Data.Abstract.Environment as Env
import Data.Abstract.Exports (Exports)
import qualified Data.Abstract.Exports as Export
import Data.Abstract.Module
import Data.Abstract.ModuleTable as ModuleTable
import Data.Abstract.Value
import Data.Coerce
import Prelude hiding (fail)
import Prologue
@ -72,43 +65,6 @@ evaluateModules [] = fail "evaluateModules: empty list"
evaluateModules (m:ms) = withModules ms (evaluateModule m)
-- | Require/import another term/file and return an Effect.
--
-- Looks up the term's name in the cache of evaluated modules first, returns a value if found, otherwise loads/evaluates the module.
require :: ( MonadAnalysis term value m
, Ord (LocationFor value)
)
=> ModuleName
-> m (EnvironmentFor value)
require name = getModuleTable >>= maybe (load name) pure . moduleTableLookup name
-- | Load another term/file and return an Effect.
--
-- Always loads/evaluates.
load :: ( MonadAnalysis term value m
, Ord (LocationFor value)
)
=> ModuleName
-> m (EnvironmentFor value)
load name = askModuleTable >>= maybe notFound evalAndCache . moduleTableLookup name
where
notFound = fail ("cannot load module: " <> show name)
evalAndCache :: (MonadAnalysis term value m, Ord (LocationFor value)) => [Module term] -> m (EnvironmentFor value)
evalAndCache [] = pure mempty
evalAndCache (x:xs) = do
void $ evaluateModule x
env <- filterEnv <$> getExports <*> getEnv
modifyModuleTable (moduleTableInsert name env)
(env <>) <$> evalAndCache xs
-- TODO: If the set of exports is empty because no exports have been
-- defined, do we export all terms, or no terms? This behavior varies across
-- languages. We need better semantics rather than doing it ad-hoc.
filterEnv :: (Ord l) => Exports l a -> Environment l a -> Environment l a
filterEnv ports env
| Export.null ports = env
| otherwise = Export.toEnvironment ports <> Env.overwrite (Export.aliases ports) env
-- | Lift a 'SubtermAlgebra' for an underlying analysis into a containing analysis. Use this when defining an analysis which can be composed onto other analyses to ensure that a call to 'analyzeTerm' occurs in the inner analysis and not the outer one.
liftAnalyze :: ( Coercible ( m term value (effects :: [* -> *]) value) (t m term value effects value)
, Coercible (t m term value effects value) ( m term value effects value)

View File

@ -13,6 +13,7 @@ module Control.Abstract.Evaluator
, MonadModuleTable(..)
, modifyModuleTable
, MonadControl(..)
, MonadThrow(..)
) where
import Data.Abstract.Address
@ -129,9 +130,9 @@ assign address = modifyHeap . heapInsert address
-- | A 'Monad' abstracting tables of modules available for import.
class Monad m => MonadModuleTable term value m | m -> term, m -> value where
-- | Retrieve the table of evaluated modules.
getModuleTable :: m (ModuleTable (EnvironmentFor value))
getModuleTable :: m (ModuleTable (EnvironmentFor value, value))
-- | Set the table of evaluated modules.
putModuleTable :: ModuleTable (EnvironmentFor value) -> m ()
putModuleTable :: ModuleTable (EnvironmentFor value, value) -> m ()
-- | Retrieve the table of unevaluated modules.
askModuleTable :: m (ModuleTable [Module term])
@ -139,7 +140,7 @@ class Monad m => MonadModuleTable term value m | m -> term, m -> value where
localModuleTable :: (ModuleTable [Module term] -> ModuleTable [Module term]) -> m a -> m a
-- | Update the evaluated module table.
modifyModuleTable :: MonadModuleTable term value m => (ModuleTable (EnvironmentFor value) -> ModuleTable (EnvironmentFor value)) -> m ()
modifyModuleTable :: MonadModuleTable term value m => (ModuleTable (EnvironmentFor value, value) -> ModuleTable (EnvironmentFor value, value)) -> m ()
modifyModuleTable f = do
table <- getModuleTable
putModuleTable $! f table
@ -153,3 +154,6 @@ class Monad m => MonadControl term m where
label :: term -> m Label
-- | “Jump” to a previously-allocated 'Label' (retrieving the @term@ at which it points, which can then be evaluated in e.g. a 'MonadAnalysis' instance).
goto :: Label -> m term
class Monad m => MonadThrow exc v m | m -> exc where
throwException :: exc -> m v

View File

@ -80,6 +80,15 @@ class (Monad m, Show value) => MonadValue value m where
-- | Construct an array of zero or more values.
array :: [value] -> m value
-- | Construct a key-value pair for use in a hash.
kvPair :: value -> value -> m value
-- | Extract the contents of a key-value pair as a tuple.
asPair :: value -> m (value, value)
-- | Construct a hash out of pairs.
hash :: [(value, value)] -> m value
-- | Extract a 'ByteString' from a given value.
asString :: value -> m ByteString
@ -87,10 +96,20 @@ class (Monad m, Show value) => MonadValue value m where
ifthenelse :: value -> m a -> m a -> m a
-- | Build a class value from a name and environment.
klass :: Name -> EnvironmentFor value -> m value
klass :: Name -- ^ The new class's identifier
-> [value] -- ^ A list of superclasses
-> EnvironmentFor value -- ^ The environment to capture
-> m value
-- | Extract the environment from a class.
objectEnvironment :: value -> m (EnvironmentFor value)
-- | Build a namespace value from a name and environment stack
--
-- Namespaces model closures with monoidal environments.
namespace :: Name -- ^ The namespace's identifier
-> EnvironmentFor value -- ^ The environment to mappend
-> m value
-- | Extract the environment from any scoped object (e.g. classes, namespaces, etc).
scopedEnvironment :: value -> m (EnvironmentFor value)
-- | Evaluate an abstraction (a binder like a lambda or method definition).
abstract :: (FreeVariables term, MonadControl term m) => [Name] -> Subterm term (m value) -> m value
@ -152,11 +171,32 @@ instance ( Monad m
multiple = pure . injValue . Value.Tuple
array = pure . injValue . Value.Array
klass n = pure . injValue . Class n
kvPair k = pure . injValue . Value.KVPair k
objectEnvironment o
asPair k
| Just (Value.KVPair k v) <- prjValue k = pure (k, v)
| otherwise = fail ("expected key-value pair, got " <> show k)
hash = pure . injValue . Value.Hash . fmap (injValue . uncurry Value.KVPair)
klass n [] env = pure . injValue $ Class n env
klass n supers env = do
product <- mconcat <$> traverse scopedEnvironment supers
pure . injValue $ Class n (Env.push product <> env)
namespace n env = do
maybeAddr <- lookupEnv n
env' <- maybe (pure mempty) (asNamespaceEnv <=< deref) maybeAddr
pure (injValue (Namespace n (env' <> env)))
where asNamespaceEnv v
| Just (Namespace _ env') <- prjValue v = pure env'
| otherwise = fail ("expected " <> show v <> " to be a namespace")
scopedEnvironment o
| Just (Class _ env) <- prjValue o = pure env
| otherwise = fail ("non-object type passed to objectEnvironment: " <> show o)
| Just (Namespace _ env) <- prjValue o = pure env
| otherwise = fail ("object type passed to scopedEnvironment doesn't have an environment: " <> show o)
asString v
| Just (Value.String n) <- prjValue v = pure n
@ -226,7 +266,7 @@ instance ( Monad m
abstract names (Subterm body _) = do
l <- label body
injValue . Closure names l . Env.bind (foldr Set.delete (freeVariables body) names) <$> getEnv
injValue . Closure names l . Env.bind (foldr Set.delete (Set.fromList (freeVariables body)) names) <$> getEnv
apply op params = do
Closure names label env <- maybe (fail ("expected a closure, got: " <> show op)) pure (prjValue op)
@ -260,11 +300,16 @@ instance (Alternative m, MonadEnvironment Type m, MonadFail m, MonadFresh m, Mon
rational _ = pure Type.Rational
multiple = pure . Type.Product
array = pure . Type.Array
klass _ _ = pure Object
hash = pure . Type.Hash
kvPair k v = pure (Product [k, v])
objectEnvironment _ = pure mempty
klass _ _ _ = pure Object
namespace _ _ = pure Type.Unit
scopedEnvironment _ = pure mempty
asString _ = fail "Must evaluate to Value to use asString"
asPair _ = fail "Must evaluate to Value to use asPair"
ifthenelse cond if' else' = unify cond Bool *> (if' <|> else')

View File

@ -1,13 +1,13 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE TypeFamilies, TypeOperators, UndecidableInstances, ScopedTypeVariables #-}
module Control.Effect where
import qualified Control.Monad.Effect as Effect
import Control.Monad.Effect as Effect
import Control.Monad.Effect.Fail
import Control.Monad.Effect.Internal
import Control.Monad.Effect.NonDetEff
import Control.Monad.Effect.NonDet
import Control.Monad.Effect.Reader
import Control.Monad.Effect.State
import Control.Monad.Effect.Writer
import Control.Monad.Effect.Resumable
import Data.Semigroup.Reducer
import Prologue
@ -58,12 +58,18 @@ instance Monoid w => RunEffect (Writer w) a where
type Result (Writer w) a = (a, w)
runEffect = runWriter
-- | 'NonDetEff' effects are interpreted into a nondeterministic set of result values.
instance Ord a => RunEffect NonDetEff a where
type Result NonDetEff a = Set a
runEffect = relay (pure . unit) (\ m k -> case m of
MZero -> pure mempty
MPlus -> mappend <$> k True <*> k False)
-- | 'NonDet' effects are interpreted into a nondeterministic set of result values.
instance Ord a => RunEffect NonDet a where
type Result NonDet a = Set a
runEffect = runNonDet unit
-- | 'Resumable' effects are interpreted into 'Either' s.t. failures are in 'Left' and successful results are in 'Right'.
instance RunEffect (Resumable exc v) a where
type Result (Resumable exc v) a = Either exc a
runEffect = runError
resumeException :: forall v m exc e a. (Effectful m, Resumable exc v :< e) => m e a -> ((v -> m e a) -> exc -> m e a) -> m e a
resumeException m handle = raise (resumeError (lower m) (\yield -> lower . handle (raise . yield)))
-- | Types wrapping 'Eff' actions.

View File

@ -1,11 +1,11 @@
{-# LANGUAGE TypeFamilies, TypeOperators, UndecidableInstances #-}
module Control.Effect.NonDet
( MonadNonDet(..)
, NonDetEff
, NonDet
) where
import Control.Monad.Effect.Internal
import Control.Monad.Effect.NonDetEff
import Control.Monad.Effect.NonDet as NonDet
import Prologue
-- | 'Monad's offering local isolation of nondeterminism effects.
@ -16,8 +16,6 @@ class (Alternative m, Monad m) => MonadNonDet m where
-> m a -- ^ The computation to run locally-nondeterministically.
-> m b -- ^ A _deterministic_ computation producing the 'Monoid'al accumulation of the _locally-nondeterministic_ result values.
-- | Effect stacks containing 'NonDetEff' offer a 'MonadNonDet' instance which implements 'gather' by interpreting the requests for nondeterminism locally, without removing 'NonDetEff' from the stack—i.e. the _capacity_ for nondeterminism is still present in the effect stack, but any local nondeterminism has been applied.
instance (NonDetEff :< fs) => MonadNonDet (Eff fs) where
gather f = interpose (pure . f) (\ m k -> case m of
MZero -> pure mempty
MPlus -> mappend <$> k True <*> k False)
-- | Effect stacks containing 'NonDet' offer a 'MonadNonDet' instance which implements 'gather' by interpreting the requests for nondeterminism locally, without removing 'NonDet' from the stack—i.e. the _capacity_ for nondeterminism is still present in the effect stack, but any local nondeterminism has been applied.
instance (NonDet :< fs) => MonadNonDet (Eff fs) where
gather = NonDet.gather

View File

@ -16,8 +16,8 @@ import Data.Abstract.Value
import Data.Functor.Classes
import Data.Proxy
import Data.Semigroup.Foldable
import Data.Semigroup.App
import Data.Term
import Prelude hiding (fail)
import Prologue
@ -28,10 +28,11 @@ class Evaluatable constr where
, MonadAnalysis term value m
, MonadValue value m
, Show (LocationFor value)
, MonadThrow Prelude.String value m
)
=> SubtermAlgebra constr term (m value)
default eval :: (MonadFail m, Show1 constr) => SubtermAlgebra constr term (m value)
eval expr = fail $ "Eval unspecialized for " ++ liftShowsPrec (const (const id)) (const id) 0 expr ""
default eval :: (MonadThrow Prelude.String value m, Show1 constr) => SubtermAlgebra constr term (m value)
eval expr = throwException $ "Eval unspecialized for " ++ liftShowsPrec (const (const id)) (const id) 0 expr ""
-- | If we can evaluate any syntax which can occur in a 'Union', we can evaluate the 'Union'.
instance Apply Evaluatable fs => Evaluatable (Union fs) where
@ -51,14 +52,4 @@ instance Evaluatable s => Evaluatable (TermF s a) where
-- 3. Only the last statements return value is returned.
instance Evaluatable [] where
-- 'nonEmpty' and 'foldMap1' enable us to return the last statements result instead of 'unit' for non-empty lists.
eval = maybe unit (runImperative . foldMap1 (Imperative . subtermValue)) . nonEmpty
-- | A 'Semigroup' providing an imperative context which extends the local environment with new bindings.
newtype Imperative m a = Imperative { runImperative :: m a }
instance MonadEnvironment value m => Semigroup (Imperative m a) where
Imperative a <> Imperative b = Imperative (a *> b)
instance (MonadEnvironment value m, MonadValue value m) => Monoid (Imperative m value) where
mempty = Imperative unit
mappend = (<>)
eval = maybe unit (runApp . foldMap1 (App . subtermValue)) . nonEmpty

View File

@ -5,7 +5,6 @@ import Prologue
import Data.Term
import Data.ByteString (intercalate)
import qualified Data.List.NonEmpty as NonEmpty
import Data.Abstract.Path
-- | The type of variable names.
type Name = NonEmpty ByteString
@ -18,10 +17,6 @@ name x = x :| []
qualifiedName :: [ByteString] -> Name
qualifiedName = NonEmpty.fromList
-- | Construct a qualified 'Name' from a `/` delimited path.
pathToQualifiedName :: ByteString -> Name
pathToQualifiedName = qualifiedName . splitOnPathSeparator
-- | User friendly 'ByteString' of a qualified 'Name'.
friendlyName :: Name -> ByteString
friendlyName xs = intercalate "." (NonEmpty.toList xs)
@ -35,7 +30,7 @@ type Label = Int
-- | Types which can contain unbound variables.
class FreeVariables term where
-- | The set of free variables in the given value.
freeVariables :: term -> Set Name
freeVariables :: term -> [Name]
-- | A lifting of 'FreeVariables' to type constructors of kind @* -> *@.
@ -43,24 +38,19 @@ class FreeVariables term where
-- 'Foldable' types requiring no additional semantics to the set of free variables (e.g. types which do not bind any variables) can use (and even derive, with @-XDeriveAnyClass@) the default implementation.
class FreeVariables1 syntax where
-- | Lift a function mapping each element to its set of free variables through a containing structure, collecting the results into a single set.
liftFreeVariables :: (a -> Set Name) -> syntax a -> Set Name
default liftFreeVariables :: (Foldable syntax) => (a -> Set Name) -> syntax a -> Set Name
liftFreeVariables :: (a -> [Name]) -> syntax a -> [Name]
default liftFreeVariables :: (Foldable syntax) => (a -> [Name]) -> syntax a -> [Name]
liftFreeVariables = foldMap
-- | Lift the 'freeVariables' method through a containing structure.
freeVariables1 :: (FreeVariables1 t, FreeVariables a) => t a -> Set Name
freeVariables1 :: (FreeVariables1 t, FreeVariables a) => t a -> [Name]
freeVariables1 = liftFreeVariables freeVariables
freeVariable :: FreeVariables term => term -> Name
freeVariable term = case toList (freeVariables term) of
freeVariable term = case freeVariables term of
[n] -> n
xs -> Prelude.fail ("expected single free variable, but got: " <> show xs)
-- TODO: Need a dedicated concept of qualified names outside of freevariables (a
-- Set) b/c you can have something like `a.a.b.a`
-- qualifiedName :: FreeVariables term => term -> Name
-- qualifiedName term = let names = toList (freeVariables term) in B.intercalate "." names
instance (FreeVariables1 syntax, Functor syntax) => FreeVariables (Term syntax ann) where
freeVariables = cata (liftFreeVariables id)

View File

@ -3,17 +3,12 @@ module Data.Abstract.Path where
import Prologue
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString as B
import Data.Char (ord)
-- | Split a 'ByteString' path on `/`, stripping quotes and any `./` prefix.
splitOnPathSeparator :: ByteString -> [ByteString]
splitOnPathSeparator = splitOnPathSeparator' id
splitOnPathSeparator' :: (ByteString -> ByteString) -> ByteString -> [ByteString]
splitOnPathSeparator' f = BC.split '/' . f . dropRelativePrefix . stripQuotes
splitOnPathSeparator = BC.split '/'
stripQuotes :: ByteString -> ByteString
stripQuotes = B.filter (/= fromIntegral (ord '\"'))
stripQuotes = B.filter (`B.notElem` "\'\"")
dropRelativePrefix :: ByteString -> ByteString
dropRelativePrefix = BC.dropWhile (== '/') . BC.dropWhile (== '.')

View File

@ -9,18 +9,19 @@ type TName = Int
-- | A datatype representing primitive types and combinations thereof.
data Type
= Int -- ^ Primitive int type.
| Bool -- ^ Primitive boolean type.
| String -- ^ Primitive string type.
| Symbol -- ^ Type of unique symbols.
| Unit -- ^ The unit type.
| Float -- ^ Floating-point type.
| Rational -- ^ Rational type.
| Type :-> Type -- ^ Binary function types.
| Var TName -- ^ A type variable.
| Product [Type] -- ^ N-ary products.
| Array [Type] -- ^ Arrays. Note that this is heterogenous.
| Object -- ^ Objects. Once we have some notion of inheritance we'll need to store a superclass.
= Int -- ^ Primitive int type.
| Bool -- ^ Primitive boolean type.
| String -- ^ Primitive string type.
| Symbol -- ^ Type of unique symbols.
| Unit -- ^ The unit type.
| Float -- ^ Floating-point type.
| Rational -- ^ Rational type.
| Type :-> Type -- ^ Binary function types.
| Var TName -- ^ A type variable.
| Product [Type] -- ^ N-ary products.
| Array [Type] -- ^ Arrays. Note that this is heterogenous.
| Hash [(Type, Type)] -- ^ Heterogenous key-value maps.
| Object -- ^ Objects. Once we have some notion of inheritance we'll need to store a superclass.
deriving (Eq, Ord, Show)
-- TODO: À la carte representation of types.

View File

@ -21,9 +21,12 @@ type ValueConstructors
, Class
, Closure
, Float
, Hash
, Integer
, String
, KVPair
, Namespace
, Rational
, String
, Symbol
, Tuple
, Unit
@ -145,6 +148,35 @@ instance Eq1 Class where liftEq = genericLiftEq
instance Ord1 Class where liftCompare = genericLiftCompare
instance Show1 Class where liftShowsPrec = genericLiftShowsPrec
data Namespace value = Namespace
{ namespaceName :: Name
, namespaceScope :: Environment Precise value
} deriving (Eq, Generic1, Ord, Show)
instance Eq1 Namespace where liftEq = genericLiftEq
instance Ord1 Namespace where liftCompare = genericLiftCompare
instance Show1 Namespace where liftShowsPrec = genericLiftShowsPrec
data KVPair value = KVPair value value
deriving (Eq, Generic1, Ord, Show)
instance Eq1 KVPair where liftEq = genericLiftEq
instance Ord1 KVPair where liftCompare = genericLiftCompare
instance Show1 KVPair where liftShowsPrec = genericLiftShowsPrec
-- You would think this would be a @Map value value@ or a @[(value, value)].
-- You would be incorrect, as we can't derive a Generic1 instance for the above,
-- and in addition a 'Map' representation would lose information given hash literals
-- that assigned multiple values to one given key. Instead, this holds KVPair
-- values. The smart constructor for hashes in MonadValue ensures that these are
-- only populated with pairs.
newtype Hash value = Hash [value]
deriving (Eq, Generic1, Ord, Show)
instance Eq1 Hash where liftEq = genericLiftEq
instance Ord1 Hash where liftCompare = genericLiftCompare
instance Show1 Hash where liftShowsPrec = genericLiftShowsPrec
-- | The environment for an abstract value type.
type EnvironmentFor v = Environment (LocationFor v) v

39
src/Data/Semigroup/App.hs Normal file
View File

@ -0,0 +1,39 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Data.Semigroup.App
( App(..)
, AppMerge(..)
) where
import Control.Applicative
import Data.Semigroup
-- $setup
-- >>> import Test.QuickCheck
-- >>> instance Arbitrary (f a) => Arbitrary (App f a) where arbitrary = App <$> arbitrary ; shrink = map App . shrink . runApp
-- >>> instance Arbitrary (f a) => Arbitrary (AppMerge f a) where arbitrary = AppMerge <$> arbitrary ; shrink = map AppMerge . shrink . runAppMerge
-- | 'Semigroup' under '*>'.
newtype App f a = App { runApp :: f a }
deriving (Alternative, Applicative, Bounded, Enum, Eq, Foldable, Functor, Monad, Ord, Show, Traversable)
-- $ Associativity:
-- prop> \ a b c -> a <> (b <> c) == (a <> b) <> (c :: App Maybe Integer)
instance Applicative f => Semigroup (App f a) where
App a <> App b = App (a *> b)
-- | 'Semigroup' and 'Monoid' under '<*>' and '<>'.
newtype AppMerge f a = AppMerge { runAppMerge :: f a }
deriving (Alternative, Applicative, Bounded, Enum, Eq, Foldable, Functor, Monad, Ord, Show, Traversable)
-- $ Associativity:
-- prop> \ a b c -> a <> (b <> c) == (a <> b) <> (c :: AppMerge Maybe String)
instance (Applicative f, Semigroup a) => Semigroup (AppMerge f a) where
AppMerge a <> AppMerge b = AppMerge ((<>) <$> a <*> b)
-- $ Identity:
-- prop> \ a -> mempty <> a == (a :: AppMerge Maybe String)
-- prop> \ a -> a <> mempty == (a :: AppMerge Maybe String)
instance (Applicative f, Monoid a, Semigroup a) => Monoid (AppMerge f a) where
mempty = AppMerge (pure mempty)
mappend = (<>)

View File

@ -7,7 +7,6 @@ import Data.Abstract.Evaluatable
import Data.AST
import Data.Range
import Data.Record
import qualified Data.Set as Set
import Data.Span
import Data.Term
import Diffing.Algorithm hiding (Empty)
@ -111,7 +110,7 @@ instance Evaluatable Identifier where
eval (Identifier name) = lookupWith deref name >>= maybe (fail ("free variable: " <> show (friendlyName name))) pure
instance FreeVariables1 Identifier where
liftFreeVariables _ (Identifier x) = Set.singleton x
liftFreeVariables _ (Identifier x) = pure x
newtype Program a = Program [a]

View File

@ -1,6 +1,7 @@
{-# LANGUAGE DeriveAnyClass, MultiParamTypeClasses, ScopedTypeVariables, UndecidableInstances #-}
module Data.Syntax.Declaration where
import Analysis.Abstract.Evaluating
import qualified Data.Abstract.Environment as Env
import Data.Abstract.Evaluatable
import Diffing.Algorithm
@ -146,10 +147,11 @@ instance Show1 Class where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Class where
eval Class{..} = do
let name = freeVariable (subterm classIdentifier)
supers <- traverse subtermValue classSuperclasses
(v, addr) <- letrec name $ do
void $ subtermValue classBody
classEnv <- Env.head <$> getEnv
klass name classEnv
klass name supers classEnv
v <$ modifyEnv (Env.insert name addr)
data Module a = Module { moduleIdentifier :: !a, moduleScope :: ![a] }
@ -243,7 +245,7 @@ instance Show1 QualifiedExportFrom where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable QualifiedExportFrom where
eval (QualifiedExportFrom from exportSymbols) = do
let moduleName = freeVariable (subterm from)
importedEnv <- isolate (require moduleName)
(importedEnv, _) <- isolate (require moduleName)
-- Look up addresses in importedEnv and insert the aliases with addresses into the exports.
for_ exportSymbols $ \(name, alias) -> do
let address = Env.lookup name importedEnv
@ -276,7 +278,7 @@ instance Show1 QualifiedImport where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable QualifiedImport where
eval (QualifiedImport from alias xs) = do
importedEnv <- isolate (require moduleName)
(importedEnv, _) <- isolate (require moduleName)
modifyEnv (mappend (Env.overwrite (renames importedEnv) importedEnv))
unit
where
@ -299,7 +301,7 @@ instance Show1 Import where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Import where
eval (Import from xs _) = do
importedEnv <- isolate (require moduleName)
(importedEnv, _) <- isolate (require moduleName)
modifyEnv (mappend (renamed importedEnv))
unit
where

View File

@ -193,7 +193,7 @@ instance Show1 MemberAccess where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable MemberAccess where
eval (fmap subtermValue -> MemberAccess mem acc) = do
lhs <- mem >>= objectEnvironment
lhs <- mem >>= scopedEnvironment
localEnv (mappend lhs) acc
-- | Subscript (e.g a[1])

View File

@ -1,6 +1,7 @@
{-# LANGUAGE DataKinds, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, TypeApplications #-}
{-# LANGUAGE DataKinds, DeriveAnyClass, DeriveGeneric, MultiParamTypeClasses, TypeApplications, ViewPatterns #-}
module Data.Syntax.Literal where
import Control.Arrow ((>>>))
import Data.Abstract.Evaluatable
import Data.ByteString.Char8 (readInteger, unpack)
import qualified Data.ByteString.Char8 as B
@ -8,7 +9,7 @@ import Data.Monoid (Endo (..), appEndo)
import Data.Scientific (Scientific)
import Diffing.Algorithm
import Prelude hiding (Float, fail)
import Prologue hiding (Set)
import Prologue hiding (Set, hash)
import Text.Read (readMaybe)
-- Boolean
@ -215,9 +216,8 @@ instance Eq1 Hash where liftEq = genericLiftEq
instance Ord1 Hash where liftCompare = genericLiftCompare
instance Show1 Hash where liftShowsPrec = genericLiftShowsPrec
-- TODO: Implement Eval instance for Hash
instance Evaluatable Hash
instance Evaluatable Hash where
eval = hashElements >>> traverse (subtermValue >=> asPair) >=> hash
data KeyValue a = KeyValue { key :: !a, value :: !a }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1)
@ -226,9 +226,9 @@ instance Eq1 KeyValue where liftEq = genericLiftEq
instance Ord1 KeyValue where liftCompare = genericLiftCompare
instance Show1 KeyValue where liftShowsPrec = genericLiftShowsPrec
-- TODO: Implement Eval instance for KeyValue
instance Evaluatable KeyValue
instance Evaluatable KeyValue where
eval (fmap subtermValue -> KeyValue{..}) =
join (kvPair <$> key <*> value)
newtype Tuple a = Tuple { tupleContents :: [a] }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1)

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, RankNTypes, TypeFamilies, TypeOperators #-}
{-# LANGUAGE RankNTypes, TypeFamilies, TypeOperators #-}
module Data.Term
( Term(..)
, termIn

View File

@ -393,7 +393,7 @@ importDeclaration = makeTerm'' <$> symbol ImportDeclaration <*> children (manyTe
namedImport = inj <$> (flip Declaration.QualifiedImport <$> packageIdentifier <*> importFromPath <*> pure [])
-- `import "lib/Math"`
plainImport = inj <$> (symbol InterpretedStringLiteral >>= \loc -> do
names <- splitOnPathSeparator <$> source
names <- toName <$> source
let from = makeTerm loc (Syntax.Identifier (qualifiedName names))
let alias = makeTerm loc (Syntax.Identifier (name (last names))) -- Go takes `import "lib/Math"` and uses `Math` as the qualified name (e.g. `Math.Sin()`)
Declaration.QualifiedImport <$> pure from <*> pure alias <*> pure [])
@ -403,7 +403,11 @@ importDeclaration = makeTerm'' <$> symbol ImportDeclaration <*> children (manyTe
underscore = makeTerm <$> symbol BlankIdentifier <*> (Literal.TextElement <$> source)
importSpec = makeTerm' <$> symbol ImportSpec <*> children (sideEffectImport <|> dotImport <|> namedImport <|> plainImport)
importSpecList = makeTerm <$> symbol ImportSpecList <*> children (manyTerm (importSpec <|> comment))
importFromPath = makeTerm <$> symbol InterpretedStringLiteral <*> (Syntax.Identifier <$> (pathToQualifiedName <$> source))
importFromPath = makeTerm <$> symbol InterpretedStringLiteral <*> (Syntax.Identifier <$> (toQualifiedName <$> source))
toQualifiedName = qualifiedName . toName
toName = splitOnPathSeparator . dropRelativePrefix . stripQuotes
indexExpression :: Assignment
indexExpression = makeTerm <$> symbol IndexExpression <*> children (Expression.Subscript <$> expression <*> manyTerm expression)

View File

@ -1,393 +1,529 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveAnyClass, ViewPatterns #-}
module Language.PHP.Syntax where
import Prologue hiding (Text)
import Analysis.Abstract.Evaluating
import Data.Abstract.Evaluatable
import Data.Abstract.Environment as Env
import Data.Abstract.Path
import Diffing.Algorithm
import Prelude hiding (fail)
import Prologue hiding (Text)
newtype Text a = Text ByteString
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Text where liftEq = genericLiftEq
instance Ord1 Text where liftCompare = genericLiftCompare
instance Show1 Text where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Text
newtype VariableName a = VariableName a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 VariableName where liftEq = genericLiftEq
instance Ord1 VariableName where liftCompare = genericLiftCompare
instance Show1 VariableName where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable VariableName
newtype RequireOnce a = RequireOnce a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
-- TODO: Variables defined in an included file take on scope of the source line
-- on which the inclusion occurs in the including file. However, functions and
-- classes defined in the included file are always in global scope.
instance Eq1 RequireOnce where liftEq = genericLiftEq
instance Ord1 RequireOnce where liftCompare = genericLiftCompare
instance Show1 RequireOnce where liftShowsPrec = genericLiftShowsPrec
-- TODO: If inclusion occurs inside a function definition within the including
-- file, the complete contents of the included file are treated as though it
-- were defined inside that function.
doInclude :: (MonadValue value m, MonadAnalysis term value m) => Subterm t (m value) -> m value
doInclude path = do
name <- toQualifiedName <$> (subtermValue path >>= asString)
(importedEnv, v) <- isolate (load name)
modifyEnv (mappend importedEnv)
pure v
doIncludeOnce :: (MonadValue value m, MonadAnalysis term value m) => Subterm t (m value) -> m value
doIncludeOnce path = do
name <- toQualifiedName <$> (subtermValue path >>= asString)
(importedEnv, v) <- isolate (require name)
modifyEnv (mappend importedEnv)
pure v
toQualifiedName :: ByteString -> Name
toQualifiedName = qualifiedName . splitOnPathSeparator . dropExtension . dropRelativePrefix . stripQuotes
newtype Require a = Require a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Require where liftEq = genericLiftEq
instance Ord1 Require where liftCompare = genericLiftCompare
instance Show1 Require where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Require where
eval (Require path) = doInclude path
newtype RequireOnce a = RequireOnce a
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 RequireOnce where liftEq = genericLiftEq
instance Ord1 RequireOnce where liftCompare = genericLiftCompare
instance Show1 RequireOnce where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable RequireOnce where
eval (RequireOnce path) = doIncludeOnce path
newtype Include a = Include a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Include where liftEq = genericLiftEq
instance Ord1 Include where liftCompare = genericLiftCompare
instance Show1 Include where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Include where
eval (Include path) = doInclude path
newtype IncludeOnce a = IncludeOnce a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 IncludeOnce where liftEq = genericLiftEq
instance Ord1 IncludeOnce where liftCompare = genericLiftCompare
instance Show1 IncludeOnce where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable IncludeOnce where
eval (IncludeOnce path) = doIncludeOnce path
newtype ArrayElement a = ArrayElement a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ArrayElement where liftEq = genericLiftEq
instance Ord1 ArrayElement where liftCompare = genericLiftCompare
instance Show1 ArrayElement where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ArrayElement
newtype GlobalDeclaration a = GlobalDeclaration [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 GlobalDeclaration where liftEq = genericLiftEq
instance Ord1 GlobalDeclaration where liftCompare = genericLiftCompare
instance Show1 GlobalDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable GlobalDeclaration
newtype SimpleVariable a = SimpleVariable a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 SimpleVariable where liftEq = genericLiftEq
instance Ord1 SimpleVariable where liftCompare = genericLiftCompare
instance Show1 SimpleVariable where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable SimpleVariable
-- | TODO: Unify with TypeScript's PredefinedType
newtype CastType a = CastType { _castType :: ByteString }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 CastType where liftEq = genericLiftEq
instance Ord1 CastType where liftCompare = genericLiftCompare
instance Show1 CastType where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable CastType
newtype ErrorControl a = ErrorControl a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ErrorControl where liftEq = genericLiftEq
instance Ord1 ErrorControl where liftCompare = genericLiftCompare
instance Show1 ErrorControl where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ErrorControl
newtype Clone a = Clone a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Clone where liftEq = genericLiftEq
instance Ord1 Clone where liftCompare = genericLiftCompare
instance Show1 Clone where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Clone
newtype ShellCommand a = ShellCommand ByteString
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ShellCommand where liftEq = genericLiftEq
instance Ord1 ShellCommand where liftCompare = genericLiftCompare
instance Show1 ShellCommand where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ShellCommand
-- | TODO: Combine with TypeScript update expression.
newtype Update a = Update { _updateSubject :: a }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Update where liftEq = genericLiftEq
instance Ord1 Update where liftCompare = genericLiftCompare
instance Show1 Update where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Update
newtype NewVariable a = NewVariable [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 NewVariable where liftEq = genericLiftEq
instance Ord1 NewVariable where liftCompare = genericLiftCompare
instance Show1 NewVariable where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable NewVariable
newtype RelativeScope a = RelativeScope ByteString
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 RelativeScope where liftEq = genericLiftEq
instance Ord1 RelativeScope where liftCompare = genericLiftCompare
instance Show1 RelativeScope where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable RelativeScope
data QualifiedName a = QualifiedName a a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
data QualifiedName a = QualifiedName !a !a
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 QualifiedName where liftEq = genericLiftEq
instance Ord1 QualifiedName where liftCompare = genericLiftCompare
instance Show1 QualifiedName where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable QualifiedName where
eval (fmap subtermValue -> QualifiedName name iden) = do
lhs <- name >>= scopedEnvironment
localEnv (mappend lhs) iden
newtype NamespaceName a = NamespaceName [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 NamespaceName where liftEq = genericLiftEq
instance Ord1 NamespaceName where liftCompare = genericLiftCompare
instance Show1 NamespaceName where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable NamespaceName where
eval (NamespaceName xs) = go xs
where
go [] = fail "nonempty NamespaceName not allowed"
go [x] = subtermValue x
go (x:xs) = do
env <- subtermValue x >>= scopedEnvironment
localEnv (mappend env) (go xs)
newtype ConstDeclaration a = ConstDeclaration [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ConstDeclaration where liftEq = genericLiftEq
instance Ord1 ConstDeclaration where liftCompare = genericLiftCompare
instance Show1 ConstDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ConstDeclaration
data ClassConstDeclaration a = ClassConstDeclaration a [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ClassConstDeclaration where liftEq = genericLiftEq
instance Ord1 ClassConstDeclaration where liftCompare = genericLiftCompare
instance Show1 ClassConstDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ClassConstDeclaration
newtype ClassInterfaceClause a = ClassInterfaceClause [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ClassInterfaceClause where liftEq = genericLiftEq
instance Ord1 ClassInterfaceClause where liftCompare = genericLiftCompare
instance Show1 ClassInterfaceClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ClassInterfaceClause
newtype ClassBaseClause a = ClassBaseClause a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ClassBaseClause where liftEq = genericLiftEq
instance Ord1 ClassBaseClause where liftCompare = genericLiftCompare
instance Show1 ClassBaseClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ClassBaseClause
newtype UseClause a = UseClause [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 UseClause where liftEq = genericLiftEq
instance Ord1 UseClause where liftCompare = genericLiftCompare
instance Show1 UseClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable UseClause
newtype ReturnType a = ReturnType a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ReturnType where liftEq = genericLiftEq
instance Ord1 ReturnType where liftCompare = genericLiftCompare
instance Show1 ReturnType where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ReturnType
newtype TypeDeclaration a = TypeDeclaration a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 TypeDeclaration where liftEq = genericLiftEq
instance Ord1 TypeDeclaration where liftCompare = genericLiftCompare
instance Show1 TypeDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable TypeDeclaration
newtype BaseTypeDeclaration a = BaseTypeDeclaration a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 BaseTypeDeclaration where liftEq = genericLiftEq
instance Ord1 BaseTypeDeclaration where liftCompare = genericLiftCompare
instance Show1 BaseTypeDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable BaseTypeDeclaration
newtype ScalarType a = ScalarType ByteString
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ScalarType where liftEq = genericLiftEq
instance Ord1 ScalarType where liftCompare = genericLiftCompare
instance Show1 ScalarType where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ScalarType
newtype EmptyIntrinsic a = EmptyIntrinsic a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 EmptyIntrinsic where liftEq = genericLiftEq
instance Ord1 EmptyIntrinsic where liftCompare = genericLiftCompare
instance Show1 EmptyIntrinsic where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable EmptyIntrinsic
newtype ExitIntrinsic a = ExitIntrinsic a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ExitIntrinsic where liftEq = genericLiftEq
instance Ord1 ExitIntrinsic where liftCompare = genericLiftCompare
instance Show1 ExitIntrinsic where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ExitIntrinsic
newtype IssetIntrinsic a = IssetIntrinsic a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 IssetIntrinsic where liftEq = genericLiftEq
instance Ord1 IssetIntrinsic where liftCompare = genericLiftCompare
instance Show1 IssetIntrinsic where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable IssetIntrinsic
newtype EvalIntrinsic a = EvalIntrinsic a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 EvalIntrinsic where liftEq = genericLiftEq
instance Ord1 EvalIntrinsic where liftCompare = genericLiftCompare
instance Show1 EvalIntrinsic where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable EvalIntrinsic
newtype PrintIntrinsic a = PrintIntrinsic a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 PrintIntrinsic where liftEq = genericLiftEq
instance Ord1 PrintIntrinsic where liftCompare = genericLiftCompare
instance Show1 PrintIntrinsic where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable PrintIntrinsic
newtype NamespaceAliasingClause a = NamespaceAliasingClause a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 NamespaceAliasingClause where liftEq = genericLiftEq
instance Ord1 NamespaceAliasingClause where liftCompare = genericLiftCompare
instance Show1 NamespaceAliasingClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable NamespaceAliasingClause
newtype NamespaceUseDeclaration a = NamespaceUseDeclaration [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 NamespaceUseDeclaration where liftEq = genericLiftEq
instance Ord1 NamespaceUseDeclaration where liftCompare = genericLiftCompare
instance Show1 NamespaceUseDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable NamespaceUseDeclaration
newtype NamespaceUseClause a = NamespaceUseClause [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 NamespaceUseClause where liftEq = genericLiftEq
instance Ord1 NamespaceUseClause where liftCompare = genericLiftCompare
instance Show1 NamespaceUseClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable NamespaceUseClause
newtype NamespaceUseGroupClause a = NamespaceUseGroupClause [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 NamespaceUseGroupClause where liftEq = genericLiftEq
instance Ord1 NamespaceUseGroupClause where liftCompare = genericLiftCompare
instance Show1 NamespaceUseGroupClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable NamespaceUseGroupClause
data Namespace a = Namespace { namespaceName :: a, namespaceBody :: a}
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
data Namespace a = Namespace { namespaceName :: a, namespaceBody :: a }
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Namespace where liftEq = genericLiftEq
instance Ord1 Namespace where liftCompare = genericLiftCompare
instance Show1 Namespace where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Namespace where
eval Namespace{..} = go names
where
names = freeVariables (subterm namespaceName)
go [] = fail "expected at least one free variable in namespaceName, found none"
-- The last name creates a closure over the namespace body.
go [name] = letrec' name $ \addr ->
subtermValue namespaceBody *> makeNamespace name addr
-- Each namespace name creates a closure over the subsequent namespace closures
go (name:xs) = letrec' name $ \addr ->
go xs <* makeNamespace name addr
-- Make a namespace closure capturing the current environment.
makeNamespace name addr = do
namespaceEnv <- Env.head <$> getEnv
v <- namespace name namespaceEnv
v <$ assign addr v
-- Lookup/alloc a name passing the address to a body evaluated in a new local environment.
letrec' name body = do
addr <- lookupOrAlloc name
v <- localEnv id (body addr)
v <$ modifyEnv (insert name addr)
data TraitDeclaration a = TraitDeclaration { traitName :: a, traitStatements :: [a] }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 TraitDeclaration where liftEq = genericLiftEq
instance Ord1 TraitDeclaration where liftCompare = genericLiftCompare
instance Show1 TraitDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable TraitDeclaration
data AliasAs a = AliasAs { aliasAsName :: a, aliasAsModifier :: a, aliasAsClause :: a }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 AliasAs where liftEq = genericLiftEq
instance Ord1 AliasAs where liftCompare = genericLiftCompare
instance Show1 AliasAs where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable AliasAs
data InsteadOf a = InsteadOf a a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 InsteadOf where liftEq = genericLiftEq
instance Ord1 InsteadOf where liftCompare = genericLiftCompare
instance Show1 InsteadOf where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable InsteadOf
newtype TraitUseSpecification a = TraitUseSpecification [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 TraitUseSpecification where liftEq = genericLiftEq
instance Ord1 TraitUseSpecification where liftCompare = genericLiftCompare
instance Show1 TraitUseSpecification where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable TraitUseSpecification
data TraitUseClause a = TraitUseClause [a] a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 TraitUseClause where liftEq = genericLiftEq
instance Ord1 TraitUseClause where liftCompare = genericLiftCompare
instance Show1 TraitUseClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable TraitUseClause
data DestructorDeclaration a = DestructorDeclaration [a] a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 DestructorDeclaration where liftEq = genericLiftEq
instance Ord1 DestructorDeclaration where liftCompare = genericLiftCompare
instance Show1 DestructorDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable DestructorDeclaration
newtype Static a = Static ByteString
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Static where liftEq = genericLiftEq
instance Ord1 Static where liftCompare = genericLiftCompare
instance Show1 Static where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Static
newtype ClassModifier a = ClassModifier ByteString
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ClassModifier where liftEq = genericLiftEq
instance Ord1 ClassModifier where liftCompare = genericLiftCompare
instance Show1 ClassModifier where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ClassModifier
data ConstructorDeclaration a = ConstructorDeclaration [a] [a] a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 ConstructorDeclaration where liftEq = genericLiftEq
instance Ord1 ConstructorDeclaration where liftCompare = genericLiftCompare
instance Show1 ConstructorDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable ConstructorDeclaration
data PropertyDeclaration a = PropertyDeclaration a [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 PropertyDeclaration where liftEq = genericLiftEq
instance Ord1 PropertyDeclaration where liftCompare = genericLiftCompare
instance Show1 PropertyDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable PropertyDeclaration
data PropertyModifier a = PropertyModifier a a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 PropertyModifier where liftEq = genericLiftEq
instance Ord1 PropertyModifier where liftCompare = genericLiftCompare
instance Show1 PropertyModifier where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable PropertyModifier
data InterfaceDeclaration a = InterfaceDeclaration a a [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 InterfaceDeclaration where liftEq = genericLiftEq
instance Ord1 InterfaceDeclaration where liftCompare = genericLiftCompare
instance Show1 InterfaceDeclaration where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable InterfaceDeclaration
newtype InterfaceBaseClause a = InterfaceBaseClause [a]
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 InterfaceBaseClause where liftEq = genericLiftEq
instance Ord1 InterfaceBaseClause where liftCompare = genericLiftCompare
instance Show1 InterfaceBaseClause where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable InterfaceBaseClause
newtype Echo a = Echo a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Echo where liftEq = genericLiftEq
instance Ord1 Echo where liftCompare = genericLiftCompare
instance Show1 Echo where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Echo
newtype Unset a = Unset a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Unset where liftEq = genericLiftEq
instance Ord1 Unset where liftCompare = genericLiftCompare
instance Show1 Unset where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Unset
data Declare a = Declare a a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 Declare where liftEq = genericLiftEq
instance Ord1 Declare where liftCompare = genericLiftCompare
instance Show1 Declare where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Declare
newtype DeclareDirective a = DeclareDirective a
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 DeclareDirective where liftEq = genericLiftEq
instance Ord1 DeclareDirective where liftCompare = genericLiftCompare
instance Show1 DeclareDirective where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable DeclareDirective
newtype LabeledStatement a = LabeledStatement { _labeledStatementIdentifier :: a }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
deriving (Diffable, Eq, Foldable, Functor, FreeVariables1, GAlign, Generic1, Mergeable, Ord, Show, Traversable)
instance Eq1 LabeledStatement where liftEq = genericLiftEq
instance Ord1 LabeledStatement where liftCompare = genericLiftCompare
instance Show1 LabeledStatement where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable LabeledStatement

View File

@ -340,12 +340,9 @@ yield = makeTerm <$> symbol Yield <*> (Statement.Yield <$> children (term ( expr
-- Identifiers and qualified identifiers (e.g. `a.b.c`) from things like DottedName and Attribute
identifier :: Assignment
identifier = makeTerm <$> (symbol Identifier <|> symbol Identifier') <*> (Syntax.Identifier <$> (name <$> source))
<|> makeQualifiedIdentifier <$> symbol Attribute <*> children (attribute <|> identifierPair)
<|> makeQualifiedIdentifier <$> symbol DottedName <*> children (some identifier')
<|> symbol DottedName *> children identifier
where
attribute = (\a b -> a <> [b]) <$> (symbol Attribute *> children (attribute <|> identifierPair)) <*> identifier'
identifierPair = (\a b -> [a, b]) <$> identifier' <*> identifier'
identifier' = (symbol Identifier <|> symbol Identifier') *> source
makeQualifiedIdentifier loc xs = makeTerm loc (Syntax.Identifier (qualifiedName xs))

View File

@ -1,11 +1,13 @@
{-# LANGUAGE DeriveAnyClass #-}
module Language.Ruby.Syntax where
import Analysis.Abstract.Evaluating
import Control.Monad (unless)
import Control.Abstract.Value (MonadValue)
import Data.Abstract.Evaluatable
import Data.Abstract.ModuleTable
import Data.Abstract.Path
import Data.Abstract.Value (LocationFor)
import Data.Abstract.Value (EnvironmentFor)
import Diffing.Algorithm
import Prelude hiding (fail)
import Prologue
@ -19,10 +21,22 @@ instance Show1 Require where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Require where
eval (Require _ x) = do
name <- pathToQualifiedName <$> (subtermValue x >>= asString)
importedEnv <- isolate (require name)
name <- toName <$> (subtermValue x >>= asString)
(importedEnv, v) <- isolate (doRequire name)
modifyEnv (mappend importedEnv)
unit
pure v -- Returns True if the file was loaded, False if it was already loaded. http://ruby-doc.org/core-2.5.0/Kernel.html#method-i-require
where
toName = qualifiedName . splitOnPathSeparator . dropRelativePrefix . stripQuotes
doRequire :: (MonadAnalysis term value m, MonadValue value m)
=> ModuleName
-> m (EnvironmentFor value, value)
doRequire name = do
moduleTable <- getModuleTable
case moduleTableLookup name moduleTable of
Nothing -> (,) <$> (fst <$> load name) <*> boolean True
Just (env, _) -> (,) <$> pure env <*> boolean False
newtype Load a = Load { loadArgs :: [a] }
deriving (Diffable, Eq, Foldable, Functor, GAlign, Generic1, Mergeable, Ord, Show, Traversable, FreeVariables1)
@ -41,12 +55,12 @@ instance Evaluatable Load where
doLoad path shouldWrap
eval (Load _) = fail "invalid argument supplied to load, path is required"
doLoad :: (MonadAnalysis term value m, MonadValue value m, Ord (LocationFor value)) => ByteString -> Bool -> m value
doLoad :: (MonadAnalysis term value m, MonadValue value m) => ByteString -> Bool -> m value
doLoad path shouldWrap = do
let name = pathToQualifiedName path
importedEnv <- isolate (load name)
(importedEnv, _) <- isolate (load (toName path))
unless shouldWrap $ modifyEnv (mappend importedEnv)
unit
where pathToQualifiedName = qualifiedName . splitOnPathSeparator' dropExtension
boolean Prelude.True -- load always returns true. http://ruby-doc.org/core-2.5.0/Kernel.html#method-i-load
where
toName = qualifiedName . splitOnPathSeparator . dropExtension . dropRelativePrefix . stripQuotes
-- TODO: autoload

View File

@ -9,6 +9,7 @@ module Language.TypeScript.Assignment
import Assigning.Assignment hiding (Assignment, Error)
import qualified Assigning.Assignment as Assignment
import Data.Abstract.FreeVariables
import Data.Abstract.Path
import Data.Record
import Data.Syntax (emptyTerm, handleError, parseError, infixContext, makeTerm, makeTerm', makeTerm'', makeTerm1, contextualize, postContextualize)
import qualified Data.Syntax as Syntax
@ -672,7 +673,9 @@ importStatement = makeImportTerm <$> symbol Grammar.ImportStatement <*> childr
makeNameAliasPair from Nothing = (from, from)
fromClause :: Assignment
fromClause = makeTerm <$> symbol Grammar.String <*> (Syntax.Identifier <$> (pathToQualifiedName <$> source))
fromClause = makeTerm <$> symbol Grammar.String <*> (Syntax.Identifier <$> (toName <$> source))
where
toName = qualifiedName . splitOnPathSeparator . dropRelativePrefix . stripQuotes
debuggerStatement :: Assignment
debuggerStatement = makeTerm <$> symbol Grammar.DebuggerStatement <*> (TypeScript.Syntax.Debugger <$ source)

View File

@ -10,64 +10,48 @@ module Prologue
import Data.Bifunctor.Join as X
import Data.Bits as X
import Data.ByteString as X (ByteString)
import Data.Functor.Both as X (Both, runBothWith, both)
import Data.Functor.Both as X (Both, both, runBothWith)
import Data.IntMap as X (IntMap)
import Data.IntSet as X (IntSet)
import Data.Ix as X (Ix(..))
import Data.Ix as X (Ix (..))
import Data.List.NonEmpty as X (NonEmpty (..), nonEmpty, some1)
import Data.Map as X (Map)
import Data.Monoid (Alt(..))
import Data.Maybe as X
import Data.Monoid (Alt (..))
import Data.Sequence as X (Seq)
import Data.Set as X (Set)
import Data.Text as X (Text)
import Data.These as X
import Data.Union as X
import Data.List.NonEmpty as X (
NonEmpty(..)
, nonEmpty
, some1
)
import Debug.Trace as X
import Control.Exception as X hiding (
evaluate
, throw
, throwIO
, throwTo
, assert
, Handler(..)
)
import Control.Exception as X hiding (Handler (..), assert, evaluate, throw, throwIO, throwTo)
-- Typeclasses
import Control.Applicative as X
import Control.Arrow as X ((&&&), (***))
import Control.Monad as X hiding (fail, return, unless, when)
import Control.Monad.Except as X (MonadError(..))
import Control.Monad.Fail as X (MonadFail(..))
import Control.Monad.Except as X (MonadError (..))
import Control.Monad.Fail as X (MonadFail (..))
import Data.Algebra as X
import Data.Align.Generic as X (GAlign)
import Data.Bifoldable as X
import Data.Bifunctor as X (Bifunctor(..))
import Data.Bifunctor as X (Bifunctor (..))
import Data.Bitraversable as X
import Data.Foldable as X hiding (product , sum)
import Data.Functor as X (($>), void)
import Data.Foldable as X hiding (product, sum)
import Data.Function as X (fix, on, (&))
import Data.Functor as X (void, ($>))
import Data.Functor.Classes as X
import Data.Functor.Classes.Generic as X
import Data.Functor.Foldable as X (Base, Recursive(..), Corecursive(..))
import Data.Functor.Foldable as X (Base, Corecursive (..), Recursive (..))
import Data.Hashable as X (Hashable, hash, hashUsing, hashWithSalt)
import Data.Mergeable as X (Mergeable)
import Data.Monoid as X (Monoid(..), First(..), Last(..))
import Data.Proxy as X (Proxy(..))
import Data.Semigroup as X (Semigroup(..))
import Data.Monoid as X (First (..), Last (..), Monoid (..))
import Data.Proxy as X (Proxy (..))
import Data.Semigroup as X (Semigroup (..))
import Data.Traversable as X
import Data.Typeable as X (Typeable)
import Data.Hashable as X (
Hashable
, hash
, hashWithSalt
, hashUsing
)
-- Generics
import GHC.Generics as X hiding (moduleName)

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Rendering.Imports
( renderToImports
, ImportSummary(..)

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
module Rendering.Symbol
( renderSymbolTerms
, renderToSymbols

View File

@ -5,7 +5,7 @@ module Semantic.Util where
import Analysis.Abstract.Caching
import Analysis.Abstract.Dead
import Analysis.Abstract.Evaluating
import Analysis.Abstract.Evaluating as X
import Analysis.Abstract.Tracing
import Analysis.Declaration
import Control.Abstract.Analysis
@ -50,6 +50,10 @@ typecheckPythonFile path = runAnalysis @(Caching Evaluating Python.Term Type) .
tracePythonFile path = runAnalysis @(Tracing [] Evaluating Python.Term Value) . evaluateModule <$> parseFile pythonParser Nothing path
evaluateDeadTracePythonFile path = runAnalysis @(DeadCode (Tracing [] Evaluating) Python.Term Value) . evaluateModule <$> parseFile pythonParser Nothing path
-- PHP
evaluatePHPFile = evaluateFile phpParser
evaluatePHPFiles = evaluateFiles phpParser
-- TypeScript
typecheckTypeScriptFile path = runAnalysis @(Caching Evaluating TypeScript.Term Type) . evaluateModule <$> parseFile typescriptParser Nothing path
evaluateTypeScriptFile = evaluateFile typescriptParser

View File

@ -1,8 +1,9 @@
{-# LANGUAGE OverloadedLists, TypeApplications #-}
{-# LANGUAGE OverloadedLists #-}
module Analysis.Go.Spec (spec) where
import Data.Abstract.Value
import Data.Map
import Data.Either
import SpecHelpers
@ -11,32 +12,27 @@ spec :: Spec
spec = parallel $ do
describe "evalutes Go" $ do
it "imports and wildcard imports" $ do
env <- evaluate "main.go"
let expectedEnv =
[ (qualifiedName ["foo", "New"], addr 0)
, (qualifiedName ["Rab"], addr 1)
, (qualifiedName ["Bar"], addr 2)
, (qualifiedName ["main"], addr 3)
]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main.go"
env `shouldBe` [ (qualifiedName ["foo", "New"], addr 0)
, (qualifiedName ["Rab"], addr 1)
, (qualifiedName ["Bar"], addr 2)
, (qualifiedName ["main"], addr 3)
]
it "imports with aliases (and side effects only)" $ do
env <- evaluate "main1.go"
let expectedEnv =
[ (qualifiedName ["f", "New"], addr 0)
, (qualifiedName ["main"], addr 3) -- addr 3 is due to side effects of
-- eval'ing `import _ "./bar"` which
-- used addr 1 & 2.
]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main1.go"
env `shouldBe` [ (qualifiedName ["f", "New"], addr 0)
, (qualifiedName ["main"], addr 3) -- addr 3 is due to side effects of
-- eval'ing `import _ "./bar"` which
-- used addr 1 & 2.
]
where
addr = Address . Precise
fixtures = "test/fixtures/go/analysis/"
evaluate entry = snd . fst . fst . fst . fst <$>
evaluateFiles goParser
[ fixtures <> entry
, fixtures <> "foo/foo.go"
, fixtures <> "bar/bar.go"
, fixtures <> "bar/rab.go"
]
evaluate entry = evaluateFiles goParser
[ fixtures <> entry
, fixtures <> "foo/foo.go"
, fixtures <> "bar/bar.go"
, fixtures <> "bar/rab.go"
]

45
test/Analysis/PHP/Spec.hs Normal file
View File

@ -0,0 +1,45 @@
{-# LANGUAGE OverloadedLists #-}
module Analysis.PHP.Spec (spec) where
import Data.Abstract.Value
import Data.Map
import Data.Map.Monoidal as Map
import SpecHelpers
spec :: Spec
spec = parallel $ do
describe "PHP" $ do
it "evaluates include and require" $ do
env <- findEnv <$> evaluate "main.php"
env `shouldBe` [ (name "foo", addr 0)
, (name "bar", addr 1) ]
it "evaluates include_once and require_once" $ do
env <- findEnv <$> evaluate "main_once.php"
env `shouldBe` [ (name "foo", addr 0)
, (name "bar", addr 1) ]
it "evaluates namespaces" $ do
res <- evaluate "namespaces.php"
findEnv res `shouldBe` [ (name "NS1", addr 0)
, (name "Foo", addr 6) ]
let heap = findHeap res
Map.lookup (Precise 0) heap `shouldBe` ns "NS1" [ (name "Sub1", addr 1)
, (name "b", addr 4)
, (name "c", addr 5)
]
Map.lookup (Precise 1) heap `shouldBe` ns "Sub1" [ (name "Sub2", addr 2) ]
Map.lookup (Precise 2) heap `shouldBe` ns "Sub2" [ (name "f", addr 3) ]
where
ns n = Just . Latest . Just . injValue . Namespace (name n)
addr = Address . Precise
fixtures = "test/fixtures/php/analysis/"
evaluate entry = evaluateFiles phpParser
[ fixtures <> entry
, fixtures <> "foo.php"
, fixtures <> "bar.php"
]

View File

@ -11,34 +11,35 @@ spec :: Spec
spec = parallel $ do
describe "evalutes Python" $ do
it "imports" $ do
env <- evaluate "main.py"
let expectedEnv =
[ (qualifiedName ["a", "foo"], addr 0)
, (qualifiedName ["b", "c", "baz"], addr 1)
]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main.py"
env `shouldBe` [ (qualifiedName ["a", "foo"], addr 0)
, (qualifiedName ["b", "c", "baz"], addr 1)
]
it "imports with aliases" $ do
env <- evaluate "main1.py"
let expectedEnv =
[ (qualifiedName ["b", "foo"], addr 0)
, (qualifiedName ["e", "baz"], addr 1)
]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main1.py"
env `shouldBe` [ (qualifiedName ["b", "foo"], addr 0)
, (qualifiedName ["e", "baz"], addr 1)
]
it "imports using 'from' syntax" $ do
env <- evaluate "main2.py"
let expectedEnv =
[ (qualifiedName ["foo"], addr 0)
, (qualifiedName ["bar"], addr 1)
]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main2.py"
env `shouldBe` [ (qualifiedName ["foo"], addr 0)
, (qualifiedName ["bar"], addr 1)
]
it "subclasses" $ do
v <- findValue <$> evaluate "subclass.py"
v `shouldBe` Right (Right (injValue (String "\"bar\"")))
it "handles multiple inheritance left-to-right" $ do
v <- findValue <$> evaluate "multiple_inheritance.py"
v `shouldBe` Right (Right (injValue (String "\"foo!\"")))
where
addr = Address . Precise
fixtures = "test/fixtures/python/analysis/"
evaluate entry = snd . fst . fst . fst . fst <$>
evaluateFiles pythonParser
evaluate entry = evaluateFiles pythonParser
[ fixtures <> entry
, fixtures <> "a.py"
, fixtures <> "b/c.py"

View File

@ -12,26 +12,26 @@ spec :: Spec
spec = parallel $ do
describe "evalutes Ruby" $ do
it "require_relative" $ do
env <- evaluate "main.rb"
let expectedEnv = [ (qualifiedName ["foo"], addr 0) ]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main.rb"
env `shouldBe` [ (qualifiedName ["foo"], addr 0) ]
it "load" $ do
env <- evaluate "load.rb"
let expectedEnv = [ (qualifiedName ["foo"], addr 0) ]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "load.rb"
env `shouldBe` [ (qualifiedName ["foo"], addr 0) ]
it "load wrap" $ do
res <- evaluate' "load-wrap.rb"
fst res `shouldBe` Left "free variable: \"foo\""
snd res `shouldBe` []
res <- evaluate "load-wrap.rb"
findValue res `shouldBe` Left "free variable: \"foo\""
findEnv res `shouldBe` []
it "subclass" $ do
v <- findValue <$> evaluate "subclass.rb"
v `shouldBe` Right (Right (injValue (String "\"<bar>\"")))
where
addr = Address . Precise
fixtures = "test/fixtures/ruby/analysis/"
evaluate entry = snd <$> evaluate' entry
evaluate' entry = fst . fst . fst . fst <$>
evaluateFiles rubyParser
evaluate entry = evaluateFiles rubyParser
[ fixtures <> entry
, fixtures <> "foo.rb"
]

View File

@ -11,38 +11,31 @@ spec :: Spec
spec = parallel $ do
describe "evalutes TypeScript" $ do
it "imports with aliased symbols" $ do
env <- evaluate "main.ts"
let expectedEnv =
[ (qualifiedName ["bar"], addr 0)
]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main.ts"
env `shouldBe` [ (qualifiedName ["bar"], addr 0) ]
it "imports with qualified names" $ do
env <- evaluate "main1.ts"
let expectedEnv =
[ (qualifiedName ["b", "baz"], addr 0)
, (qualifiedName ["b", "foo"], addr 2)
, (qualifiedName ["z", "baz"], addr 0)
, (qualifiedName ["z", "foo"], addr 2)
]
env `shouldBe` expectedEnv
env <- findEnv <$> evaluate "main1.ts"
env `shouldBe` [ (qualifiedName ["b", "baz"], addr 0)
, (qualifiedName ["b", "foo"], addr 2)
, (qualifiedName ["z", "baz"], addr 0)
, (qualifiedName ["z", "foo"], addr 2)
]
it "side effect only imports" $ do
env <- evaluate "main2.ts"
env <- findEnv <$> evaluate "main2.ts"
env `shouldBe` mempty
it "fails exporting symbols not defined in the module" $ do
env <- fst <$> evaluate' "bad-export.ts"
env `shouldBe` Left "module \"foo\" does not export \"pip\""
v <- findValue <$> evaluate "bad-export.ts"
v `shouldBe` Left "module \"foo\" does not export \"pip\""
where
addr = Address . Precise
fixtures = "test/fixtures/typescript/analysis/"
evaluate entry = snd <$> evaluate' entry
evaluate' entry = fst . fst . fst . fst <$>
evaluateFiles typescriptParser
[ fixtures <> entry
, fixtures <> "a.ts"
, fixtures <> "foo.ts"
, fixtures <> "pip.ts"
]
evaluate entry = evaluateFiles typescriptParser
[ fixtures <> entry
, fixtures <> "a.ts"
, fixtures <> "foo.ts"
, fixtures <> "pip.ts"
]

View File

@ -156,7 +156,7 @@ instance Listable1 f => Listable2 (FreeF f) where
instance (Listable1 f, Listable a) => Listable1 (FreeF f a) where
liftTiers = liftTiers2 tiers
instance (Functor f, Listable1 f) => Listable1 (Free.Free f) where
instance Listable1 f => Listable1 (Free.Free f) where
liftTiers pureTiers = go
where go = liftCons1 (liftTiers2 pureTiers go) free
free (FreeF.Free f) = Free.Free f

View File

@ -12,13 +12,16 @@ main = do
extensions :: [String]
extensions =
[ "DeriveFoldable"
[ "DataKinds"
, "DeriveFoldable"
, "DeriveFunctor"
, "DeriveGeneric"
, "DeriveTraversable"
, "FlexibleContexts"
, "FlexibleInstances"
, "MultiParamTypeClasses"
, "OverloadedStrings"
, "RecordWildCards"
, "StandaloneDeriving"
, "StrictData"
]

View File

@ -1,6 +1,7 @@
module Main where
import qualified Analysis.Go.Spec
import qualified Analysis.PHP.Spec
import qualified Analysis.Python.Spec
import qualified Analysis.Ruby.Spec
import qualified Analysis.TypeScript.Spec
@ -27,6 +28,7 @@ main = hspec $ do
describe "Semantic.Stat" Semantic.Stat.Spec.spec
parallel $ do
describe "Analysis.Go" Analysis.Go.Spec.spec
describe "Analysis.PHP" Analysis.PHP.Spec.spec
describe "Analysis.Python" Analysis.Python.Spec.spec
describe "Analysis.Ruby" Analysis.Ruby.Spec.spec
describe "Analysis.TypeScript" Analysis.TypeScript.Spec.spec

View File

@ -1,4 +1,4 @@
{-# LANGUAGE DataKinds, GADTs, ScopedTypeVariables, TypeFamilies, TypeOperators, TypeApplications #-}
{-# LANGUAGE GADTs, ScopedTypeVariables, TypeFamilies, TypeOperators, TypeApplications #-}
module SpecHelpers (
module X
, diffFilePaths
@ -9,6 +9,7 @@ module SpecHelpers (
, Verbatim(..)
, ) where
import Analysis.Abstract.Evaluating as X (findValue, findEnv, findHeap)
import Data.Abstract.Address as X
import Data.Abstract.FreeVariables as X hiding (dropExtension)
import Data.Abstract.Heap as X
@ -34,6 +35,7 @@ import Data.Functor.Both as X (Both, runBothWith, both)
import Data.Maybe as X
import Data.Monoid as X (Monoid(..), First(..), Last(..))
import Data.Semigroup as X (Semigroup(..))
import Control.Monad as X
import Test.Hspec as X (Spec, SpecWith, context, describe, it, xit, parallel, pendingWith, around, runIO)
import Test.Hspec.Expectations.Pretty as X
@ -43,6 +45,7 @@ import Test.LeanCheck as X
import qualified Data.ByteString as B
import qualified Semantic.IO as IO
-- | Returns an s-expression formatted diff for the specified FilePath pair.
diffFilePaths :: Both FilePath -> IO ByteString
diffFilePaths paths = readFilePair paths >>= runTask . diffBlobPair SExpressionDiffRenderer

4
test/fixtures/php/analysis/bar.php vendored Normal file
View File

@ -0,0 +1,4 @@
<?php
function bar() {
return 1;
}

4
test/fixtures/php/analysis/foo.php vendored Normal file
View File

@ -0,0 +1,4 @@
<?php
function foo() {
return 1;
}

6
test/fixtures/php/analysis/main.php vendored Normal file
View File

@ -0,0 +1,6 @@
<?php
include 'foo.php';
require 'bar.php';
foo();
bar();

View File

@ -0,0 +1,6 @@
<?php
include_once 'foo.php';
require_once 'bar.php';
foo();
bar();

View File

@ -0,0 +1,28 @@
<?php
namespace NS1\Sub1\Sub2
{
function f() {
return 1;
}
}
namespace NS1
{
function b() {
return "hi";
}
}
namespace NS1
{
function c() {
return "x";
}
}
namespace Foo
{
\NS1\Sub1\Sub2\f();
\NS1\b();
\NS1\c();
}

View File

@ -0,0 +1,12 @@
class Foo:
def dang(self):
return "foo!"
class Bar:
def dang(self):
return "bar!"
class Baz(Foo, Bar): pass
Baz.dang()

View File

@ -0,0 +1,9 @@
class Foo():
def dang():
return "foo"
class Bar():
def dang():
return "bar"
Bar.dang()

View File

@ -14,6 +14,8 @@
{+(Identifier)+}
(Empty))
{+(Call
{+(Identifier)+}
{+(MemberAccess
{+(Identifier)+}
{+(Identifier)+})+}
{+(Integer)+}
{+(Empty)+})+})

View File

@ -14,6 +14,8 @@
{+(Identifier)+}
(Empty))
{-(Call
{-(Identifier)-}
{-(MemberAccess
{-(Identifier)-}
{-(Identifier)-})-}
{-(Integer)-}
{-(Empty)-})-})

View File

@ -12,6 +12,8 @@
(Identifier)
(Empty))
(Call
(Identifier)
(MemberAccess
(Identifier)
(Identifier))
(Integer)
(Empty)))

View File

@ -269,8 +269,10 @@
22
]
},
"symbol": "os.getcwd",
"targets": []
"symbol": "getcwd",
"targets": [
"os"
]
},
{
"span": {
@ -283,8 +285,10 @@
31
]
},
"symbol": "np.array",
"targets": []
"symbol": "array",
"targets": [
"np"
]
},
{
"span": {
@ -297,8 +301,10 @@
20
]
},
"symbol": "x.sum",
"targets": []
"symbol": "sum",
"targets": [
"x"
]
},
{
"span": {
@ -311,8 +317,10 @@
19
]
},
"symbol": "x.sum",
"targets": []
"symbol": "sum",
"targets": [
"x"
]
},
{
"span": {

View File

@ -25,7 +25,9 @@
{+(Let
{+(Empty)+}
{+(Call
{+(Identifier)+}
{+(MemberAccess
{+(Identifier)+}
{+(Identifier)+})+}
{+(Identifier)+}
{+(TextElement)+}
{+(Identifier)+}
@ -34,7 +36,9 @@
{+(Let
{+(Empty)+}
{+(Call
{+(Identifier)+}
{+(MemberAccess
{+(Identifier)+}
{+(Identifier)+})+}
{+(Identifier)+}
{+(TextElement)+}
{+(Identifier)+}

View File

@ -25,7 +25,9 @@
{-(Let
{-(Empty)-}
{-(Call
{-(Identifier)-}
{-(MemberAccess
{-(Identifier)-}
{-(Identifier)-})-}
{-(Identifier)-}
{-(TextElement)-}
{-(Identifier)-}
@ -34,7 +36,9 @@
{-(Let
{-(Empty)-}
{-(Call
{-(Identifier)-}
{-(MemberAccess
{-(Identifier)-}
{-(Identifier)-})-}
{-(Identifier)-}
{-(TextElement)-}
{-(Identifier)-}

View File

@ -12,7 +12,9 @@
(Let
(Empty)
(Call
(Identifier)
(MemberAccess
(Identifier)
(Identifier))
(Identifier)
(TextElement)
(Identifier)
@ -21,7 +23,9 @@
(Let
(Empty)
(Call
(Identifier)
(MemberAccess
(Identifier)
(Identifier))
(Identifier)
(TextElement)
(Identifier)

13
test/fixtures/ruby/analysis/subclass.rb vendored Normal file
View File

@ -0,0 +1,13 @@
class Foo
def inspect
"<foo>"
end
end
class Bar < Foo
def inspect
"<bar>"
end
end
Bar.inspect()

2
vendor/effects vendored

@ -1 +1 @@
Subproject commit 6aaaa39f18f38628a91d3ffd155c7f4099131d9e
Subproject commit 7d4525db7b6a12c0a34da5f70a97db137c144c60

1
vendor/ghc-mod vendored

@ -1 +0,0 @@
Subproject commit 7fb380dae0ae877a24ac8258fcd193cd6256a171