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

Merge branch 'master' into haskell-assignment

This commit is contained in:
Rick Winfrey 2018-05-31 11:22:45 -05:00 committed by GitHub
commit 9016553aaa
36 changed files with 240 additions and 287 deletions

View File

@ -17,8 +17,8 @@ error "Avoid return" =
return ==> pure return ==> pure
where note = "return is obsolete as of GHC 7.10" where note = "return is obsolete as of GHC 7.10"
error "use extract" = termAnnotation . unTerm ==> extract error "use termAnnotation" = termFAnnotation . unTerm ==> termAnnotation
error "use unwrap" = termOut . unTerm ==> unwrap error "use termOut" = termFOut . unTerm ==> termOut
error "avoid head" = head error "avoid head" = head
where note = "head is partial; consider using Data.Maybe.listToMaybe" where note = "head is partial; consider using Data.Maybe.listToMaybe"
@ -31,3 +31,5 @@ error "avoid init" = init
error "avoid last" = last error "avoid last" = last
where note = "last is partial; consider pattern-matching" where note = "last is partial; consider pattern-matching"
error "use maybeM" = maybe a pure ==> maybeM a

View File

@ -40,7 +40,6 @@ library
, Control.Abstract.Context , Control.Abstract.Context
, Control.Abstract.Environment , Control.Abstract.Environment
, Control.Abstract.Evaluator , Control.Abstract.Evaluator
, Control.Abstract.Exports
, Control.Abstract.Heap , Control.Abstract.Heap
, Control.Abstract.Hole , Control.Abstract.Hole
, Control.Abstract.Matching , Control.Abstract.Matching

View File

@ -62,7 +62,7 @@ cachingTerms :: ( Cacheable term address (Cell address) value
, Member (Reader (Cache term address (Cell address) value)) effects , Member (Reader (Cache term address (Cell address) value)) effects
, Member (Reader (Live address)) effects , Member (Reader (Live address)) effects
, Member (State (Cache term address (Cell address) value)) effects , Member (State (Cache term address (Cell address) value)) effects
, Member (State (Environment address)) effects , Member (Env address) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
) )
=> SubtermAlgebra (Base term) term (TermEvaluator term address value effects (ValueRef value)) => SubtermAlgebra (Base term) term (TermEvaluator term address value effects (ValueRef value))
@ -82,11 +82,10 @@ convergingModules :: ( AbstractValue address value effects
, Member Fresh effects , Member Fresh effects
, Member NonDet effects , Member NonDet effects
, Member (Reader (Cache term address (Cell address) value)) effects , Member (Reader (Cache term address (Cell address) value)) effects
, Member (Reader (Environment address)) effects
, Member (Reader (Live address)) effects , Member (Reader (Live address)) effects
, Member (Resumable (EnvironmentError address)) effects , Member (Resumable (EnvironmentError address)) effects
, Member (State (Cache term address (Cell address) value)) effects , Member (State (Cache term address (Cell address) value)) effects
, Member (State (Environment address)) effects , Member (Env address) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
) )
=> SubtermAlgebra Module term (TermEvaluator term address value effects value) => SubtermAlgebra Module term (TermEvaluator term address value effects value)
@ -94,8 +93,7 @@ convergingModules :: ( AbstractValue address value effects
convergingModules recur m = do convergingModules recur m = do
c <- getConfiguration (subterm (moduleBody m)) c <- getConfiguration (subterm (moduleBody m))
-- Convergence here is predicated upon an Eq instance, not α-equivalence -- Convergence here is predicated upon an Eq instance, not α-equivalence
cache <- converge lowerBound (\ prevCache -> isolateCache $ do cache <- converge lowerBound (\ prevCache -> isolateCache $ raiseHandler locally $ do
TermEvaluator (putEnv (configurationEnvironment c))
TermEvaluator (putHeap (configurationHeap c)) TermEvaluator (putHeap (configurationHeap c))
-- We need to reset fresh generation so that this invocation converges. -- We need to reset fresh generation so that this invocation converges.
resetFresh 0 $ resetFresh 0 $

View File

@ -5,14 +5,13 @@ module Analysis.Abstract.Evaluating
) where ) where
import Control.Abstract import Control.Abstract
import Control.Monad.Effect.Fail
import Data.Semilattice.Lower import Data.Semilattice.Lower
-- | An analysis evaluating @term@s to @value@s with a list of @effects@ using 'Evaluatable', and producing incremental results of type @a@. -- | An analysis evaluating @term@s to @value@s with a list of @effects@ using 'Evaluatable', and producing incremental results of type @a@.
data EvaluatingState address value = EvaluatingState data EvaluatingState address value = EvaluatingState
{ environment :: Environment address { heap :: Heap address (Cell address) value
, heap :: Heap address (Cell address) value
, modules :: ModuleTable (Maybe (Environment address, value)) , modules :: ModuleTable (Maybe (Environment address, value))
, exports :: Exports address
} }
deriving instance (Eq (Cell address value), Eq address, Eq value) => Eq (EvaluatingState address value) deriving instance (Eq (Cell address value), Eq address, Eq value) => Eq (EvaluatingState address value)
@ -23,19 +22,13 @@ deriving instance (Show (Cell address value), Show address, Show value) => Show
evaluating :: Evaluator address value evaluating :: Evaluator address value
( Fail ( Fail
': Fresh ': Fresh
': Reader (Environment address)
': State (Environment address)
': State (Heap address (Cell address) value) ': State (Heap address (Cell address) value)
': State (ModuleTable (Maybe (Environment address, value))) ': State (ModuleTable (Maybe (Environment address, value)))
': State (Exports address)
': effects) result ': effects) result
-> Evaluator address value effects (Either String result, EvaluatingState address value) -> Evaluator address value effects (Either String result, EvaluatingState address value)
evaluating evaluating
= fmap (\ ((((result, env), heap), modules), exports) -> (result, EvaluatingState env heap modules exports)) = fmap (\ ((result, heap), modules) -> (result, EvaluatingState heap modules))
. runState lowerBound -- State (Exports address)
. runState lowerBound -- State (ModuleTable (Maybe (Environment address, value))) . runState lowerBound -- State (ModuleTable (Maybe (Environment address, value)))
. runState lowerBound -- State (Heap address (Cell address) value) . runState lowerBound -- State (Heap address (Cell address) value)
. runState lowerBound -- State (Environment address)
. runReader lowerBound -- Reader (Environment address)
. runFresh 0 . runFresh 0
. runFail . runFail

View File

@ -52,9 +52,8 @@ style = (defaultStyle (byteString . vertexName))
-- | Add vertices to the graph for evaluated identifiers. -- | Add vertices to the graph for evaluated identifiers.
graphingTerms :: ( Element Syntax.Identifier syntax graphingTerms :: ( Element Syntax.Identifier syntax
, Member (Reader (Environment (Hole (Located address)))) effects
, Member (Reader ModuleInfo) effects , Member (Reader ModuleInfo) effects
, Member (State (Environment (Hole (Located address)))) effects , Member (Env (Hole (Located address))) effects
, Member (State (Graph Vertex)) effects , Member (State (Graph Vertex)) effects
, term ~ Term (Sum syntax) ann , term ~ Term (Sum syntax) ann
) )
@ -121,8 +120,7 @@ moduleInclusion v = do
appendGraph (vertex (moduleVertex m) `connect` vertex v) appendGraph (vertex (moduleVertex m) `connect` vertex v)
-- | Add an edge from the passed variable name to the module it originated within. -- | Add an edge from the passed variable name to the module it originated within.
variableDefinition :: ( Member (Reader (Environment (Hole (Located address)))) effects variableDefinition :: ( Member (Env (Hole (Located address))) effects
, Member (State (Environment (Hole (Located address)))) effects
, Member (State (Graph Vertex)) effects , Member (State (Graph Vertex)) effects
) )
=> Name => Name

View File

@ -14,7 +14,7 @@ import Prologue
-- Instantiating @trace@ to @[]@ yields a linear trace analysis, while @Set@ yields a reachable state analysis. -- Instantiating @trace@ to @[]@ yields a linear trace analysis, while @Set@ yields a reachable state analysis.
tracingTerms :: ( Corecursive term tracingTerms :: ( Corecursive term
, Member (Reader (Live address)) effects , Member (Reader (Live address)) effects
, Member (State (Environment address)) effects , Member (Env address) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Member (Writer (trace (Configuration term address (Cell address) value))) effects , Member (Writer (trace (Configuration term address (Cell address) value))) effects
, Reducer (Configuration term address (Cell address) value) (trace (Configuration term address (Cell address) value)) , Reducer (Configuration term address (Cell address) value) (trace (Configuration term address (Cell address) value))

View File

@ -5,9 +5,8 @@ module Control.Abstract
import Control.Abstract.Addressable as X import Control.Abstract.Addressable as X
import Control.Abstract.Configuration as X import Control.Abstract.Configuration as X
import Control.Abstract.Context as X import Control.Abstract.Context as X
import Control.Abstract.Environment as X import Control.Abstract.Environment as X hiding (Lookup)
import Control.Abstract.Evaluator as X import Control.Abstract.Evaluator as X
import Control.Abstract.Exports as X
import Control.Abstract.Heap as X import Control.Abstract.Heap as X
import Control.Abstract.Hole as X import Control.Abstract.Hole as X
import Control.Abstract.Modules as X import Control.Abstract.Modules as X

View File

@ -12,5 +12,5 @@ import Control.Abstract.TermEvaluator
import Data.Abstract.Configuration import Data.Abstract.Configuration
-- | Get the current 'Configuration' with a passed-in term. -- | Get the current 'Configuration' with a passed-in term.
getConfiguration :: (Member (Reader (Live address)) effects, Member (State (Environment address)) effects, Member (State (Heap address (Cell address) value)) effects) => term -> TermEvaluator term address value effects (Configuration term address (Cell address) value) getConfiguration :: (Member (Reader (Live address)) effects, Member (Env address) effects, Member (State (Heap address (Cell address) value)) effects) => term -> TermEvaluator term address value effects (Configuration term address (Cell address) value)
getConfiguration term = Configuration term <$> TermEvaluator askRoots <*> TermEvaluator getEnv <*> TermEvaluator getHeap getConfiguration term = Configuration term <$> TermEvaluator askRoots <*> TermEvaluator getEnv <*> TermEvaluator getHeap

View File

@ -1,14 +1,16 @@
{-# LANGUAGE GADTs, RankNTypes, TypeOperators #-} {-# LANGUAGE GADTs, LambdaCase, RankNTypes, ScopedTypeVariables, TypeOperators #-}
module Control.Abstract.Environment module Control.Abstract.Environment
( Environment ( Environment
, Exports
, getEnv , getEnv
, putEnv , export
, withEnv
, withDefaultEnvironment
, lookupEnv , lookupEnv
, bind , bind
, bindAll , bindAll
, locally , locally
, close
, Env(..)
, runEnv
, EnvironmentError(..) , EnvironmentError(..)
, freeVariableError , freeVariableError
, runEnvironmentError , runEnvironmentError
@ -16,54 +18,78 @@ module Control.Abstract.Environment
) where ) where
import Control.Abstract.Evaluator import Control.Abstract.Evaluator
import Data.Abstract.Environment as Env import Data.Abstract.Environment (Environment)
import qualified Data.Abstract.Environment as Env
import Data.Abstract.Exports as Exports
import Data.Abstract.Name import Data.Abstract.Name
import Data.Semilattice.Lower
import Prologue import Prologue
-- | Retrieve the environment. -- | Retrieve the environment.
getEnv :: Member (State (Environment address)) effects => Evaluator address value effects (Environment address) getEnv :: Member (Env address) effects => Evaluator address value effects (Environment address)
getEnv = get getEnv = send GetEnv
-- | Set the environment. -- | Add an export to the global export state.
putEnv :: Member (State (Environment address)) effects => Environment address -> Evaluator address value effects () export :: Member (Env address) effects => Name -> Name -> Maybe address -> Evaluator address value effects ()
putEnv = put export name alias addr = send (Export name alias addr)
-- | Update the global environment.
modifyEnv :: Member (State (Environment address)) effects => (Environment address -> Environment address) -> Evaluator address value effects ()
modifyEnv = modify'
-- | Sets the environment for the lifetime of the given action.
withEnv :: Member (State (Environment address)) effects => Environment address -> Evaluator address value effects a -> Evaluator address value effects a
withEnv = localState . const
-- | Retrieve the default environment.
defaultEnvironment :: Member (Reader (Environment address)) effects => Evaluator address value effects (Environment address)
defaultEnvironment = ask
-- | Set the default environment for the lifetime of an action.
-- Usually only invoked in a top-level evaluation function.
withDefaultEnvironment :: Member (Reader (Environment address)) effects => Environment address -> Evaluator address value effects a -> Evaluator address value effects a
withDefaultEnvironment e = local (const e)
-- | Look a 'Name' up in the current environment, trying the default environment if no value is found. -- | Look a 'Name' up in the current environment, trying the default environment if no value is found.
lookupEnv :: (Member (Reader (Environment address)) effects, Member (State (Environment address)) effects) => Name -> Evaluator address value effects (Maybe address) lookupEnv :: Member (Env address) effects => Name -> Evaluator address value effects (Maybe address)
lookupEnv name = (<|>) <$> (Env.lookup name <$> getEnv) <*> (Env.lookup name <$> defaultEnvironment) lookupEnv name = send (Lookup name)
-- | Bind a 'Name' to an 'Address' in the current scope. -- | Bind a 'Name' to an address in the current scope.
bind :: Member (State (Environment address)) effects => Name -> address -> Evaluator address value effects () bind :: Member (Env address) effects => Name -> address -> Evaluator address value effects ()
bind name = modifyEnv . Env.insert name bind name addr = send (Bind name addr)
-- | Bind all of the names from an 'Environment' in the current scope. -- | Bind all of the names from an 'Environment' in the current scope.
bindAll :: Member (State (Environment address)) effects => Environment address -> Evaluator address value effects () bindAll :: Member (Env address) effects => Environment address -> Evaluator address value effects ()
bindAll = foldr ((>>) . uncurry bind) (pure ()) . pairs bindAll = foldr ((>>) . uncurry bind) (pure ()) . Env.pairs
-- | Run an action in a new local environment. -- | Run an action in a new local scope.
locally :: Member (State (Environment address)) effects => Evaluator address value effects a -> Evaluator address value effects a locally :: forall address value effects a . Member (Env address) effects => Evaluator address value effects a -> Evaluator address value effects a
locally a = do locally a = do
modifyEnv Env.push send (Push @address)
a' <- a a' <- a
a' <$ modifyEnv Env.pop a' <$ send (Pop @address)
close :: Member (Env address) effects => Set Name -> Evaluator address value effects (Environment address)
close = send . Close
data Env address return where
Lookup :: Name -> Env address (Maybe address)
Bind :: Name -> address -> Env address ()
Close :: Set Name -> Env address (Environment address)
Push :: Env address ()
Pop :: Env address ()
GetEnv :: Env address (Environment address)
Export :: Name -> Name -> Maybe address -> Env address ()
handleEnv :: forall address effects value result
. ( Member (State (Environment address)) effects
, Member (State (Exports address)) effects
)
=> Env address result
-> Evaluator address value effects result
handleEnv = \case
Lookup name -> Env.lookup name <$> get
Bind name addr -> modify (Env.insert name addr)
Close names -> Env.intersect names <$> get
Push -> modify (Env.push @address)
Pop -> modify (Env.pop @address)
GetEnv -> get
Export name alias addr -> modify (Exports.insert name alias addr)
runEnv :: Environment address
-> Evaluator address value (Env address ': effects) a
-> Evaluator address value effects (a, Environment address)
runEnv initial = fmap (uncurry filterEnv . first (fmap Env.head)) . runState lowerBound . runState (Env.push initial) . reinterpret2 handleEnv
where -- 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 (a, env) ports
| Exports.null ports = (a, env)
| otherwise = (a, Exports.toEnvironment ports `Env.mergeEnvs` Env.overwrite (Exports.aliases ports) env)
-- | Errors involving the environment. -- | Errors involving the environment.

View File

@ -1,32 +0,0 @@
module Control.Abstract.Exports
( Exports
, getExports
, putExports
, modifyExports
, addExport
, withExports
) where
import Control.Abstract.Evaluator
import Data.Abstract.Exports
import Data.Abstract.Name
-- | Get the global export state.
getExports :: Member (State (Exports address)) effects => Evaluator address value effects (Exports address)
getExports = get
-- | Set the global export state.
putExports :: Member (State (Exports address)) effects => Exports address -> Evaluator address value effects ()
putExports = put
-- | Update the global export state.
modifyExports :: Member (State (Exports address)) effects => (Exports address -> Exports address) -> Evaluator address value effects ()
modifyExports = modify'
-- | Add an export to the global export state.
addExport :: Member (State (Exports address)) effects => Name -> Name -> Maybe address -> Evaluator address value effects ()
addExport name alias = modifyExports . insert name alias
-- | Sets the global export state for the lifetime of the given action.
withExports :: Member (State (Exports address)) effects => Exports address -> Evaluator address value effects a -> Evaluator address value effects a
withExports = localState . const

View File

@ -62,17 +62,15 @@ assign address = modifyHeap . heapInsert address
-- | Look up or allocate an address for a 'Name'. -- | Look up or allocate an address for a 'Name'.
lookupOrAlloc :: ( Member (Allocator address value) effects lookupOrAlloc :: ( Member (Allocator address value) effects
, Member (Reader (Environment address)) effects , Member (Env address) effects
, Member (State (Environment address)) effects
) )
=> Name => Name
-> Evaluator address value effects address -> Evaluator address value effects address
lookupOrAlloc name = lookupEnv name >>= maybe (alloc name) pure lookupOrAlloc name = lookupEnv name >>= maybeM (alloc name)
letrec :: ( Member (Allocator address value) effects letrec :: ( Member (Allocator address value) effects
, Member (Reader (Environment address)) effects , Member (Env address) effects
, Member (State (Environment address)) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Ord address , Ord address
, Reducer value (Cell address value) , Reducer value (Cell address value)
@ -88,8 +86,7 @@ letrec name body = do
-- Lookup/alloc a name passing the address to a body evaluated in a new local environment. -- Lookup/alloc a name passing the address to a body evaluated in a new local environment.
letrec' :: ( Member (Allocator address value) effects letrec' :: ( Member (Allocator address value) effects
, Member (Reader (Environment address)) effects , Member (Env address) effects
, Member (State (Environment address)) effects
) )
=> Name => Name
-> (address -> Evaluator address value effects value) -> (address -> Evaluator address value effects value)
@ -102,9 +99,8 @@ letrec' name body = do
-- | Look up and dereference the given 'Name', throwing an exception for free variables. -- | Look up and dereference the given 'Name', throwing an exception for free variables.
variable :: ( Member (Allocator address value) effects variable :: ( Member (Allocator address value) effects
, Member (Reader (Environment address)) effects , Member (Env address) effects
, Member (Resumable (EnvironmentError address)) effects , Member (Resumable (EnvironmentError address)) effects
, Member (State (Environment address)) effects
) )
=> Name => Name
-> Evaluator address value effects value -> Evaluator address value effects value

View File

@ -23,6 +23,7 @@ import Data.Abstract.Environment
import Data.Abstract.Module import Data.Abstract.Module
import Data.Abstract.ModuleTable as ModuleTable import Data.Abstract.ModuleTable as ModuleTable
import Data.Language import Data.Language
import Data.Tuple (swap)
import Prologue import Prologue
-- | Retrieve an evaluated module, if any. The outer 'Maybe' indicates whether weve begun loading the module or not, while the inner 'Maybe' indicates whether weve completed loading it or not. Thus, @Nothing@ means weve never tried to load it, @Just Nothing@ means weve started but havent yet finished loading it, and @Just (Just (env, value))@ indicates the result of a completed load. -- | Retrieve an evaluated module, if any. The outer 'Maybe' indicates whether weve begun loading the module or not, while the inner 'Maybe' indicates whether weve completed loading it or not. Thus, @Nothing@ means weve never tried to load it, @Just Nothing@ means weve started but havent yet finished loading it, and @Just (Just (env, value))@ indicates the result of a completed load.
@ -47,11 +48,11 @@ require path = lookupModule path >>= maybeM (load path)
-- --
-- Always loads/evaluates. -- Always loads/evaluates.
load :: Member (Modules address value) effects => ModulePath -> Evaluator address value effects (Maybe (Environment address, value)) load :: Member (Modules address value) effects => ModulePath -> Evaluator address value effects (Maybe (Environment address, value))
load = send . Load load path = fmap swap <$> send (Load path)
data Modules address value return where data Modules address value return where
Load :: ModulePath -> Modules address value (Maybe (Environment address, value)) Load :: ModulePath -> Modules address value (Maybe (value, Environment address))
Lookup :: ModulePath -> Modules address value (Maybe (Maybe (Environment address, value))) Lookup :: ModulePath -> Modules address value (Maybe (Maybe (Environment address, value)))
Resolve :: [FilePath] -> Modules address value (Maybe ModulePath) Resolve :: [FilePath] -> Modules address value (Maybe ModulePath)
List :: FilePath -> Modules address value [ModulePath] List :: FilePath -> Modules address value [ModulePath]
@ -64,7 +65,7 @@ runModules :: forall term address value effects a
, Member (State (ModuleTable (Maybe (Environment address, value)))) effects , Member (State (ModuleTable (Maybe (Environment address, value)))) effects
, Member Trace effects , Member Trace effects
) )
=> (Module term -> Evaluator address value (Modules address value ': effects) (Environment address, value)) => (Module term -> Evaluator address value (Modules address value ': effects) (value, Environment address))
-> Evaluator address value (Modules address value ': effects) a -> Evaluator address value (Modules address value ': effects) a
-> Evaluator address value (Reader (ModuleTable [Module term]) ': effects) a -> Evaluator address value (Reader (ModuleTable [Module term]) ': effects) a
runModules evaluateModule = go runModules evaluateModule = go
@ -92,19 +93,19 @@ runModules evaluateModule = go
getModuleTable :: Member (State (ModuleTable (Maybe (Environment address, value)))) effects => Evaluator address value effects (ModuleTable (Maybe (Environment address, value))) getModuleTable :: Member (State (ModuleTable (Maybe (Environment address, value)))) effects => Evaluator address value effects (ModuleTable (Maybe (Environment address, value)))
getModuleTable = get getModuleTable = get
cacheModule :: Member (State (ModuleTable (Maybe (Environment address, value)))) effects => ModulePath -> Maybe (Environment address, value) -> Evaluator address value effects (Maybe (Environment address, value)) cacheModule :: Member (State (ModuleTable (Maybe (Environment address, value)))) effects => ModulePath -> Maybe (value, Environment address) -> Evaluator address value effects (Maybe (value, Environment address))
cacheModule path result = modify' (ModuleTable.insert path result) $> result cacheModule path result = modify' (ModuleTable.insert path (swap <$> result)) $> result
askModuleTable :: Member (Reader (ModuleTable [Module term])) effects => Evaluator address value effects (ModuleTable [Module term]) askModuleTable :: Member (Reader (ModuleTable [Module term])) effects => Evaluator address value effects (ModuleTable [Module term])
askModuleTable = ask askModuleTable = ask
newtype Merging m address value = Merging { runMerging :: m (Maybe (Environment address, value)) } newtype Merging m address value = Merging { runMerging :: m (Maybe (value, Environment address)) }
instance Applicative m => Semigroup (Merging m address value) where instance Applicative m => Semigroup (Merging m address value) where
Merging a <> Merging b = Merging (merge <$> a <*> b) Merging a <> Merging b = Merging (merge <$> a <*> b)
where merge a b = mergeJusts <$> a <*> b <|> a <|> b where merge a b = mergeJusts <$> a <*> b <|> a <|> b
mergeJusts (env1, _) (env2, v) = (mergeEnvs env1 env2, v) mergeJusts (_, env1) (v, env2) = (v, mergeEnvs env1 env2)
instance Applicative m => Monoid (Merging m address value) where instance Applicative m => Monoid (Merging m address value) where
mappend = (<>) mappend = (<>)
@ -113,7 +114,7 @@ instance Applicative m => Monoid (Merging m address value) where
-- | An error thrown when loading a module from the list of provided modules. Indicates we weren't able to find a module with the given name. -- | An error thrown when loading a module from the list of provided modules. Indicates we weren't able to find a module with the given name.
data LoadError address value resume where data LoadError address value resume where
ModuleNotFound :: ModulePath -> LoadError address value (Maybe (Environment address, value)) ModuleNotFound :: ModulePath -> LoadError address value (Maybe (value, Environment address))
deriving instance Eq (LoadError address value resume) deriving instance Eq (LoadError address value resume)
deriving instance Show (LoadError address value resume) deriving instance Show (LoadError address value resume)
@ -122,7 +123,7 @@ instance Show1 (LoadError address value) where
instance Eq1 (LoadError address value) where instance Eq1 (LoadError address value) where
liftEq _ (ModuleNotFound a) (ModuleNotFound b) = a == b liftEq _ (ModuleNotFound a) (ModuleNotFound b) = a == b
moduleNotFound :: Member (Resumable (LoadError address value)) effects => ModulePath -> Evaluator address value effects (Maybe (Environment address, value)) moduleNotFound :: Member (Resumable (LoadError address value)) effects => ModulePath -> Evaluator address value effects (Maybe (value, Environment address))
moduleNotFound = throwResumable . ModuleNotFound moduleNotFound = throwResumable . ModuleNotFound
resumeLoadError :: Member (Resumable (LoadError address value)) effects => Evaluator address value effects a -> (forall resume . LoadError address value resume -> Evaluator address value effects resume) -> Evaluator address value effects a resumeLoadError :: Member (Resumable (LoadError address value)) effects => Evaluator address value effects a -> (forall resume . LoadError address value resume -> Evaluator address value effects resume) -> Evaluator address value effects a

View File

@ -14,9 +14,9 @@ import Prologue
builtin :: ( HasCallStack builtin :: ( HasCallStack
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Env address) effects
, Member (Reader ModuleInfo) effects , Member (Reader ModuleInfo) effects
, Member (Reader Span) effects , Member (Reader Span) effects
, Member (State (Environment address)) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Ord address , Ord address
, Reducer value (Cell address value) , Reducer value (Cell address value)
@ -40,12 +40,11 @@ lambda body = do
defineBuiltins :: ( AbstractValue address value effects defineBuiltins :: ( AbstractValue address value effects
, HasCallStack , HasCallStack
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Env address) effects
, Member Fresh effects , Member Fresh effects
, Member (Reader (Environment address)) effects
, Member (Reader ModuleInfo) effects , Member (Reader ModuleInfo) effects
, Member (Reader Span) effects , Member (Reader Span) effects
, Member (Resumable (EnvironmentError address)) effects , Member (Resumable (EnvironmentError address)) effects
, Member (State (Environment address)) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Member Trace effects , Member Trace effects
, Ord address , Ord address

View File

@ -7,7 +7,6 @@ module Control.Abstract.TermEvaluator
import Control.Abstract.Evaluator import Control.Abstract.Evaluator
import Control.Monad.Effect as X import Control.Monad.Effect as X
import Control.Monad.Effect.Fail as X
import Control.Monad.Effect.Fresh as X import Control.Monad.Effect.Fresh as X
import Control.Monad.Effect.NonDet as X import Control.Monad.Effect.NonDet as X
import Control.Monad.Effect.Reader as X import Control.Monad.Effect.Reader as X

View File

@ -158,7 +158,7 @@ asBool value = ifthenelse value (pure True) (pure False)
-- | C-style for loops. -- | C-style for loops.
forLoop :: ( AbstractValue address value effects forLoop :: ( AbstractValue address value effects
, Member (State (Environment address)) effects , Member (Env address) effects
) )
=> Evaluator address value effects value -- ^ Initial statement => Evaluator address value effects value -- ^ Initial statement
-> Evaluator address value effects value -- ^ Condition -> Evaluator address value effects value -- ^ Condition
@ -187,7 +187,7 @@ doWhile body cond = loop $ \ continue -> body *> do
ifthenelse this continue (pure unit) ifthenelse this continue (pure unit)
makeNamespace :: ( AbstractValue address value effects makeNamespace :: ( AbstractValue address value effects
, Member (State (Environment address)) effects , Member (Env address) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Ord address , Ord address
, Reducer value (Cell address value) , Reducer value (Cell address value)
@ -206,7 +206,7 @@ makeNamespace name addr super = do
-- | Evaluate a term within the context of the scoped environment of 'scopedEnvTerm'. -- | Evaluate a term within the context of the scoped environment of 'scopedEnvTerm'.
evaluateInScopedEnv :: ( AbstractValue address value effects evaluateInScopedEnv :: ( AbstractValue address value effects
, Member (State (Environment address)) effects , Member (Env address) effects
) )
=> Evaluator address value effects value => Evaluator address value effects value
-> Evaluator address value effects value -> Evaluator address value effects value
@ -219,9 +219,8 @@ evaluateInScopedEnv scopedEnvTerm term = do
-- | Evaluates a 'Value' returning the referenced value -- | Evaluates a 'Value' returning the referenced value
value :: ( AbstractValue address value effects value :: ( AbstractValue address value effects
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Reader (Environment address)) effects , Member (Env address) effects
, Member (Resumable (EnvironmentError address)) effects , Member (Resumable (EnvironmentError address)) effects
, Member (State (Environment address)) effects
) )
=> ValueRef value => ValueRef value
-> Evaluator address value effects value -> Evaluator address value effects value
@ -232,9 +231,8 @@ value (Rval val) = pure val
-- | Evaluates a 'Subterm' to its rval -- | Evaluates a 'Subterm' to its rval
subtermValue :: ( AbstractValue address value effects subtermValue :: ( AbstractValue address value effects
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Reader (Environment address)) effects , Member (Env address) effects
, Member (Resumable (EnvironmentError address)) effects , Member (Resumable (EnvironmentError address)) effects
, Member (State (Environment address)) effects
) )
=> Subterm term (Evaluator address value effects (ValueRef value)) => Subterm term (Evaluator address value effects (ValueRef value))
-> Evaluator address value effects value -> Evaluator address value effects value

View File

@ -1,7 +1,6 @@
module Data.Abstract.Environment module Data.Abstract.Environment
( Environment(..) ( Environment(..)
, addresses , addresses
, intersect
, delete , delete
, head , head
, emptyEnv , emptyEnv
@ -10,6 +9,7 @@ module Data.Abstract.Environment
, insert , insert
, lookup , lookup
, names , names
, intersect
, overwrite , overwrite
, pairs , pairs
, unpairs , unpairs

View File

@ -3,7 +3,6 @@ module Data.Abstract.Evaluatable
( module X ( module X
, Evaluatable(..) , Evaluatable(..)
, evaluatePackageWith , evaluatePackageWith
, isolate
, traceResolve , traceResolve
-- | Effects -- | Effects
, EvalError(..) , EvalError(..)
@ -20,13 +19,11 @@ import Control.Abstract
import Control.Abstract.Context as X import Control.Abstract.Context as X
import Control.Abstract.Environment as X hiding (runEnvironmentError, runEnvironmentErrorWith) import Control.Abstract.Environment as X hiding (runEnvironmentError, runEnvironmentErrorWith)
import Control.Abstract.Evaluator as X hiding (LoopControl(..), Return(..), catchLoopControl, runLoopControl, catchReturn, runReturn) import Control.Abstract.Evaluator as X hiding (LoopControl(..), Return(..), catchLoopControl, runLoopControl, catchReturn, runReturn)
import Control.Abstract.Exports as X
import Control.Abstract.Heap as X hiding (AddressError(..), runAddressError, runAddressErrorWith) import Control.Abstract.Heap as X hiding (AddressError(..), runAddressError, runAddressErrorWith)
import Control.Abstract.Modules as X (Modules, ResolutionError(..), load, lookupModule, listModulesInDir, require, resolve) import Control.Abstract.Modules as X (Modules, ResolutionError(..), load, lookupModule, listModulesInDir, require, resolve)
import Control.Abstract.Value as X import Control.Abstract.Value as X
import Data.Abstract.Declarations as X import Data.Abstract.Declarations as X
import Data.Abstract.Environment as X import Data.Abstract.Environment as X
import Data.Abstract.Exports as Exports
import Data.Abstract.FreeVariables as X import Data.Abstract.FreeVariables as X
import Data.Abstract.Module import Data.Abstract.Module
import Data.Abstract.ModuleTable as ModuleTable import Data.Abstract.ModuleTable as ModuleTable
@ -56,9 +53,9 @@ type EvaluatableConstraints address term value effects =
, Declarations term , Declarations term
, FreeVariables term , FreeVariables term
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Env address) effects
, Member (LoopControl value) effects , Member (LoopControl value) effects
, Member (Modules address value) effects , Member (Modules address value) effects
, Member (Reader (Environment address)) effects
, Member (Reader ModuleInfo) effects , Member (Reader ModuleInfo) effects
, Member (Reader PackageInfo) effects , Member (Reader PackageInfo) effects
, Member (Reader Span) effects , Member (Reader Span) effects
@ -67,8 +64,6 @@ type EvaluatableConstraints address term value effects =
, Member (Resumable ResolutionError) effects , Member (Resumable ResolutionError) effects
, Member (Resumable (Unspecialized value)) effects , Member (Resumable (Unspecialized value)) effects
, Member (Return value) effects , Member (Return value) effects
, Member (State (Environment address)) effects
, Member (State (Exports address)) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Member Trace effects , Member Trace effects
, Ord address , Ord address
@ -77,75 +72,65 @@ type EvaluatableConstraints address term value effects =
-- | Evaluate a given package. -- | Evaluate a given package.
evaluatePackageWith :: forall address term value inner outer evaluatePackageWith :: forall address term value inner inner' inner'' outer
-- FIXME: Itd be nice if we didnt have to mention 'Addressable' here at all, but 'Located' addresses require knowledge of 'currentModule' to run. Can we fix that? If not, can we factor this effect list out? -- FIXME: Itd be nice if we didnt have to mention 'Addressable' here at all, but 'Located' locations require knowledge of 'currentModule' to run. Can we fix that?
. ( Addressable address (Reader ModuleInfo ': Modules address value ': Reader Span ': Reader PackageInfo ': outer) . ( Addressable address inner'
, Evaluatable (Base term) , Evaluatable (Base term)
, EvaluatableConstraints address term value inner , EvaluatableConstraints address term value inner
, Member Fail outer , Member Fail outer
, Member Fresh outer , Member Fresh outer
, Member (Reader (Environment address)) outer
, Member (Resumable (AddressError address value)) outer , Member (Resumable (AddressError address value)) outer
, Member (Resumable (LoadError address value)) outer , Member (Resumable (LoadError address value)) outer
, Member (State (Environment address)) outer
, Member (State (Exports address)) outer
, Member (State (Heap address (Cell address) value)) outer , Member (State (Heap address (Cell address) value)) outer
, Member (State (ModuleTable (Maybe (Environment address, value)))) outer , Member (State (ModuleTable (Maybe (Environment address, value)))) outer
, Member Trace outer , Member Trace outer
, Recursive term , Recursive term
, inner ~ (LoopControl value ': Return value ': Allocator address value ': Reader ModuleInfo ': Modules address value ': Reader Span ': Reader PackageInfo ': outer) , inner ~ (LoopControl value ': Return value ': Env address ': Allocator address value ': inner')
, inner' ~ (Reader ModuleInfo ': inner'')
, inner'' ~ (Modules address value ': Reader Span ': Reader PackageInfo ': outer)
) )
=> (SubtermAlgebra Module term (TermEvaluator term address value inner value) -> SubtermAlgebra Module term (TermEvaluator term address value inner value)) => (SubtermAlgebra Module term (TermEvaluator term address value inner value) -> SubtermAlgebra Module term (TermEvaluator term address value inner value))
-> (SubtermAlgebra (Base term) term (TermEvaluator term address value inner (ValueRef value)) -> SubtermAlgebra (Base term) term (TermEvaluator term address value inner (ValueRef value))) -> (SubtermAlgebra (Base term) term (TermEvaluator term address value inner (ValueRef value)) -> SubtermAlgebra (Base term) term (TermEvaluator term address value inner (ValueRef value)))
-> Package term -> Package term
-> TermEvaluator term address value outer [value] -> TermEvaluator term address value outer [(value, Environment address)]
evaluatePackageWith analyzeModule analyzeTerm package evaluatePackageWith analyzeModule analyzeTerm package
= runReader (packageInfo package) = runReader (packageInfo package)
. runReader lowerBound . runReader lowerBound
. runReader (packageModules (packageBody package)) . runReader (packageModules (packageBody package))
. withPrelude (packagePrelude (packageBody package)) . withPrelude (packagePrelude (packageBody package))
. raiseHandler (runModules (runTermEvaluator . evalModule)) $ \ preludeEnv
$ traverse (uncurry evaluateEntryPoint) (ModuleTable.toPairs (packageEntryPoints (packageBody package))) -> raiseHandler (runModules (runTermEvaluator . evalModule preludeEnv))
. traverse (uncurry (evaluateEntryPoint preludeEnv))
$ ModuleTable.toPairs (packageEntryPoints (packageBody package))
where where
evalModule m evalModule preludeEnv m
= pairValueWithEnv = runInModule preludeEnv (moduleInfo m)
. runInModule (moduleInfo m)
. analyzeModule (subtermRef . moduleBody) . analyzeModule (subtermRef . moduleBody)
$ evalTerm <$> m $ evalTerm <$> m
evalTerm term = Subterm term (TermEvaluator (value =<< runTermEvaluator (foldSubterms (analyzeTerm (TermEvaluator . eval . fmap (second runTermEvaluator))) term))) evalTerm term = Subterm term (TermEvaluator (value =<< runTermEvaluator (foldSubterms (analyzeTerm (TermEvaluator . eval . fmap (second runTermEvaluator))) term)))
runInModule info runInModule preludeEnv info
= runReader info = runReader info
. raiseHandler runAllocator . raiseHandler runAllocator
. raiseHandler (runEnv preludeEnv)
. raiseHandler runReturn . raiseHandler runReturn
. raiseHandler runLoopControl . raiseHandler runLoopControl
evaluateEntryPoint :: ModulePath -> Maybe Name -> TermEvaluator term address value (Modules address value ': Reader Span ': Reader PackageInfo ': outer) value evaluateEntryPoint :: Environment address -> ModulePath -> Maybe Name -> TermEvaluator term address value inner'' (value, Environment address)
evaluateEntryPoint m sym = runInModule (ModuleInfo m) . TermEvaluator $ do evaluateEntryPoint preludeEnv m sym = runInModule preludeEnv (ModuleInfo m) . TermEvaluator $ do
v <- maybe unit snd <$> require m (env, value) <- fromMaybe (emptyEnv, unit) <$> require m
maybe (pure v) ((`call` []) <=< variable) sym bindAll env
maybe (pure value) ((`call` []) <=< variable) sym
evalPrelude prelude = raiseHandler (runModules (runTermEvaluator . evalModule)) $ do evalPrelude prelude = raiseHandler (runModules (runTermEvaluator . evalModule emptyEnv)) $ do
_ <- runInModule moduleInfoFromCallStack (TermEvaluator (defineBuiltins $> unit)) (_, builtinsEnv) <- runInModule emptyEnv moduleInfoFromCallStack (TermEvaluator (defineBuiltins $> unit))
fst <$> evalModule prelude second (mergeEnvs builtinsEnv) <$> evalModule builtinsEnv prelude
withPrelude Nothing a = a withPrelude Nothing f = f emptyEnv
withPrelude (Just prelude) a = do withPrelude (Just prelude) f = do
preludeEnv <- evalPrelude prelude (_, preludeEnv) <- evalPrelude prelude
raiseHandler (withDefaultEnvironment preludeEnv) a f preludeEnv
-- 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 ports env
| Exports.null ports = env
| otherwise = Exports.toEnvironment ports `mergeEnvs` overwrite (Exports.aliases ports) env
pairValueWithEnv action = flip (,) <$> action <*> (filterEnv <$> TermEvaluator getExports <*> TermEvaluator getEnv)
-- | Isolate the given action with an empty global environment and exports.
isolate :: (Member (State (Environment address)) effects, Member (State (Exports address)) effects) => Evaluator address value effects a -> Evaluator address value effects a
isolate = withEnv lowerBound . withExports lowerBound
traceResolve :: (Show a, Show b, Member Trace effects) => a -> b -> Evaluator address value effects () traceResolve :: (Show a, Show b, Member Trace effects) => a -> b -> Evaluator address value effects ()
traceResolve name path = trace ("resolved " <> show name <> " -> " <> show path) traceResolve name path = trace ("resolved " <> show name <> " -> " <> show path)

View File

@ -12,3 +12,6 @@ data ValueRef value where
-- | An object member. -- | An object member.
LvalMember :: value -> Name -> ValueRef value LvalMember :: value -> Name -> ValueRef value
deriving (Eq, Ord, Show) deriving (Eq, Ord, Show)
newtype Ref address value = Ref address

View File

@ -117,21 +117,21 @@ instance AbstractIntro Type where
instance ( Member (Allocator address Type) effects instance ( Member (Allocator address Type) effects
, Member (Env address) effects
, Member Fresh effects , Member Fresh effects
, Member (Resumable TypeError) effects , Member (Resumable TypeError) effects
, Member (Return Type) effects , Member (Return Type) effects
, Member (State (Environment address)) effects , Member (State (Heap address (Cell address) Type)) effects
, Member (State (Heap address (Cell address) Type)) effects
, Ord address , Ord address
, Reducer Type (Cell address Type) , Reducer Type (Cell address Type)
) )
=> AbstractFunction address Type effects where => AbstractFunction address Type effects where
closure names _ body = do closure names _ body = do
(env, tvars) <- foldr (\ name rest -> do (env, tvars) <- foldr (\ name rest -> do
a <- alloc name addr <- alloc name
tvar <- Var <$> fresh tvar <- Var <$> fresh
assign a tvar assign addr tvar
bimap (Env.insert name a) (tvar :) <$> rest) (pure (emptyEnv, [])) names bimap (Env.insert name addr) (tvar :) <$> rest) (pure (emptyEnv, [])) names
(zeroOrMoreProduct tvars :->) <$> locally (bindAll env *> body `catchReturn` \ (Return value) -> pure value) (zeroOrMoreProduct tvars :->) <$> locally (bindAll env *> body `catchReturn` \ (Return value) -> pure value)
call op params = do call op params = do
@ -146,12 +146,12 @@ instance ( Member (Allocator address Type) effects
-- | Discard the value arguments (if any), constructing a 'Type' instead. -- | Discard the value arguments (if any), constructing a 'Type' instead.
instance ( Member (Allocator address Type) effects instance ( Member (Allocator address Type) effects
, Member (Env address) effects
, Member Fresh effects , Member Fresh effects
, Member NonDet effects , Member NonDet effects
, Member (Resumable TypeError) effects , Member (Resumable TypeError) effects
, Member (Return Type) effects , Member (Return Type) effects
, Member (State (Environment address)) effects , Member (State (Heap address (Cell address) Type)) effects
, Member (State (Heap address (Cell address) Type)) effects
, Ord address , Ord address
, Reducer Type (Cell address Type) , Reducer Type (Cell address Type)
) )

View File

@ -42,7 +42,7 @@ instance Ord (ClosureBody address body) where
compare = compare `on` closureBodyId compare = compare `on` closureBodyId
instance Show (ClosureBody address body) where instance Show (ClosureBody address body) where
showsPrec d (ClosureBody i _) = showsBinaryWith showsPrec (const showChar) "ClosureBody" d i '_' showsPrec d (ClosureBody i _) = showsUnaryWith showsPrec "ClosureBody" d i
instance Ord address => ValueRoots address (Value address body) where instance Ord address => ValueRoots address (Value address body) where
@ -56,12 +56,12 @@ instance AbstractHole (Value address body) where
instance ( Coercible body (Eff effects) instance ( Coercible body (Eff effects)
, Member (Allocator address (Value address body)) effects , Member (Allocator address (Value address body)) effects
, Member (Env address) effects
, Member Fresh effects , Member Fresh effects
, Member (Reader ModuleInfo) effects , Member (Reader ModuleInfo) effects
, Member (Reader PackageInfo) effects , Member (Reader PackageInfo) effects
, Member (Resumable (ValueError address body)) effects , Member (Resumable (ValueError address body)) effects
, Member (Return (Value address body)) effects , Member (Return (Value address body)) effects
, Member (State (Environment address)) effects
, Member (State (Heap address (Cell address) (Value address body))) effects , Member (State (Heap address (Cell address) (Value address body))) effects
, Ord address , Ord address
, Reducer (Value address body) (Cell address (Value address body)) , Reducer (Value address body) (Cell address (Value address body))
@ -72,7 +72,7 @@ instance ( Coercible body (Eff effects)
packageInfo <- currentPackage packageInfo <- currentPackage
moduleInfo <- currentModule moduleInfo <- currentModule
i <- fresh i <- fresh
Closure packageInfo moduleInfo parameters (ClosureBody i (coerce (lowerEff body))) . Env.intersect (foldr Set.delete freeVariables parameters) <$> getEnv Closure packageInfo moduleInfo parameters (ClosureBody i (coerce (lowerEff body))) <$> close (foldr Set.delete freeVariables parameters)
call op params = do call op params = do
case op of case op of
@ -81,10 +81,10 @@ instance ( Coercible body (Eff effects)
-- charge them to the closure's origin. -- charge them to the closure's origin.
withCurrentPackage packageInfo . withCurrentModule moduleInfo $ do withCurrentPackage packageInfo . withCurrentModule moduleInfo $ do
bindings <- foldr (\ (name, param) rest -> do bindings <- foldr (\ (name, param) rest -> do
v <- param value <- param
a <- alloc name addr <- alloc name
assign a v assign addr value
Env.insert name a <$> rest) (pure env) (zip names params) Env.insert name addr <$> rest) (pure env) (zip names params)
locally (bindAll bindings *> raiseEff (coerce body) `catchReturn` \ (Return value) -> pure value) locally (bindAll bindings *> raiseEff (coerce body) `catchReturn` \ (Return value) -> pure value)
_ -> throwValueError (CallError op) _ -> throwValueError (CallError op)
@ -109,14 +109,13 @@ instance Show address => AbstractIntro (Value address body) where
-- | Construct a 'Value' wrapping the value arguments (if any). -- | Construct a 'Value' wrapping the value arguments (if any).
instance ( Coercible body (Eff effects) instance ( Coercible body (Eff effects)
, Member (Allocator address (Value address body)) effects , Member (Allocator address (Value address body)) effects
, Member (Env address) effects
, Member Fresh effects , Member Fresh effects
, Member (LoopControl (Value address body)) effects , Member (LoopControl (Value address body)) effects
, Member (Reader (Environment address)) effects
, Member (Reader ModuleInfo) effects , Member (Reader ModuleInfo) effects
, Member (Reader PackageInfo) effects , Member (Reader PackageInfo) effects
, Member (Resumable (ValueError address body)) effects , Member (Resumable (ValueError address body)) effects
, Member (Return (Value address body)) effects , Member (Return (Value address body)) effects
, Member (State (Environment address)) effects
, Member (State (Heap address (Cell address) (Value address body))) effects , Member (State (Heap address (Cell address) (Value address body))) effects
, Ord address , Ord address
, Reducer (Value address body) (Cell address (Value address body)) , Reducer (Value address body) (Cell address (Value address body))

View File

@ -27,8 +27,7 @@ instance Evaluatable Function where
eval Function{..} = do eval Function{..} = do
name <- either (throwEvalError . FreeVariablesError) pure (freeVariable $ subterm functionName) name <- either (throwEvalError . FreeVariablesError) pure (freeVariable $ subterm functionName)
(v, addr) <- letrec name (closure (paramNames functionParameters) (Set.fromList (freeVariables functionBody)) (subtermValue functionBody)) (v, addr) <- letrec name (closure (paramNames functionParameters) (Set.fromList (freeVariables functionBody)) (subtermValue functionBody))
bind name addr Rval v <$ bind name addr
pure (Rval v)
where paramNames = foldMap (freeVariables . subterm) where paramNames = foldMap (freeVariables . subterm)
instance Declarations a => Declarations (Function a) where instance Declarations a => Declarations (Function a) where
@ -53,8 +52,7 @@ instance Evaluatable Method where
eval Method{..} = do eval Method{..} = do
name <- either (throwEvalError . FreeVariablesError) pure (freeVariable $ subterm methodName) name <- either (throwEvalError . FreeVariablesError) pure (freeVariable $ subterm methodName)
(v, addr) <- letrec name (closure (paramNames methodParameters) (Set.fromList (freeVariables methodBody)) (subtermValue methodBody)) (v, addr) <- letrec name (closure (paramNames methodParameters) (Set.fromList (freeVariables methodBody)) (subtermValue methodBody))
bind name addr Rval v <$ bind name addr
pure (Rval v)
where paramNames = foldMap (freeVariables . subterm) where paramNames = foldMap (freeVariables . subterm)

View File

@ -221,7 +221,7 @@ instance Diffable [] where
-- | Diff two non-empty lists using RWS. -- | Diff two non-empty lists using RWS.
instance Diffable NonEmpty where instance Diffable NonEmpty where
algorithmFor (a1:|as1) (a2:|as2) = nonEmpty <$> byRWS (a1:as1) (a2:as2) >>= maybe empty pure algorithmFor (a1:|as1) (a2:|as2) = nonEmpty <$> byRWS (a1:as1) (a2:as2) >>= maybeM empty
tryAlignWith f (a1:|as1) (a2:|as2) = (:|) <$> f (These a1 a2) <*> tryAlignWith f as1 as2 tryAlignWith f (a1:|as1) (a2:|as2) = (:|) <$> f (These a1 a2) <*> tryAlignWith f as1 as2

View File

@ -70,7 +70,7 @@ instance Evaluatable Import where
paths <- resolveGoImport importPath paths <- resolveGoImport importPath
for_ paths $ \path -> do for_ paths $ \path -> do
traceResolve (unPath importPath) path traceResolve (unPath importPath) path
importedEnv <- maybe emptyEnv fst <$> isolate (require path) importedEnv <- maybe emptyEnv fst <$> require path
bindAll importedEnv bindAll importedEnv
pure (Rval unit) pure (Rval unit)
@ -94,7 +94,7 @@ instance Evaluatable QualifiedImport where
void $ letrec' alias $ \addr -> do void $ letrec' alias $ \addr -> do
for_ paths $ \p -> do for_ paths $ \p -> do
traceResolve (unPath importPath) p traceResolve (unPath importPath) p
importedEnv <- maybe emptyEnv fst <$> isolate (require p) importedEnv <- maybe emptyEnv fst <$> require p
bindAll importedEnv bindAll importedEnv
makeNamespace alias addr Nothing makeNamespace alias addr Nothing
pure (Rval unit) pure (Rval unit)
@ -113,7 +113,7 @@ instance Evaluatable SideEffectImport where
eval (SideEffectImport importPath _) = do eval (SideEffectImport importPath _) = do
paths <- resolveGoImport importPath paths <- resolveGoImport importPath
traceResolve (unPath importPath) paths traceResolve (unPath importPath) paths
for_ paths $ \path -> isolate (require path) for_ paths require
pure (Rval unit) pure (Rval unit)
-- A composite literal in Go -- A composite literal in Go

View File

@ -48,18 +48,16 @@ resolvePHPName :: ( Member (Modules address value) effects
-> Evaluator address value effects ModulePath -> Evaluator address value effects ModulePath
resolvePHPName n = do resolvePHPName n = do
modulePath <- resolve [name] modulePath <- resolve [name]
maybe (throwResumable $ NotFoundError name [name] Language.PHP) pure modulePath maybeM (throwResumable $ NotFoundError name [name] Language.PHP) modulePath
where name = toName n where name = toName n
toName = BC.unpack . dropRelativePrefix . stripQuotes toName = BC.unpack . dropRelativePrefix . stripQuotes
include :: ( AbstractValue address value effects include :: ( AbstractValue address value effects
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Env address) effects
, Member (Modules address value) effects , Member (Modules address value) effects
, Member (Reader (Environment address)) effects
, Member (Resumable ResolutionError) effects , Member (Resumable ResolutionError) effects
, Member (Resumable (EnvironmentError address)) effects , Member (Resumable (EnvironmentError address)) effects
, Member (State (Environment address)) effects
, Member (State (Exports address)) effects
, Member Trace effects , Member Trace effects
) )
=> Subterm term (Evaluator address value effects (ValueRef value)) => Subterm term (Evaluator address value effects (ValueRef value))
@ -69,7 +67,7 @@ include pathTerm f = do
name <- subtermValue pathTerm >>= asString name <- subtermValue pathTerm >>= asString
path <- resolvePHPName name path <- resolvePHPName name
traceResolve name path traceResolve name path
(importedEnv, v) <- isolate (f path) >>= maybeM (pure (emptyEnv, unit)) (importedEnv, v) <- fromMaybe (emptyEnv, unit) <$> f path
bindAll importedEnv bindAll importedEnv
pure (Rval v) pure (Rval v)

View File

@ -82,7 +82,7 @@ resolvePythonModules q = do
, path <.> ".py" , path <.> ".py"
] ]
modulePath <- resolve searchPaths modulePath <- resolve searchPaths
maybe (throwResumable $ NotFoundError path searchPaths Language.Python) pure modulePath maybeM (throwResumable $ NotFoundError path searchPaths Language.Python) modulePath
-- | Import declarations (symbols are added directly to the calling environment). -- | Import declarations (symbols are added directly to the calling environment).
@ -113,11 +113,11 @@ instance Evaluatable Import where
modulePaths <- resolvePythonModules name modulePaths <- resolvePythonModules name
-- Eval parent modules first -- Eval parent modules first
for_ (NonEmpty.init modulePaths) (isolate . require) for_ (NonEmpty.init modulePaths) require
-- Last module path is the one we want to import -- Last module path is the one we want to import
let path = NonEmpty.last modulePaths let path = NonEmpty.last modulePaths
importedEnv <- maybe emptyEnv fst <$> isolate (require path) importedEnv <- maybe emptyEnv fst <$> require path
bindAll (select importedEnv) bindAll (select importedEnv)
pure (Rval unit) pure (Rval unit)
where where
@ -129,17 +129,15 @@ instance Evaluatable Import where
-- Evaluate a qualified import -- Evaluate a qualified import
evalQualifiedImport :: ( AbstractValue address value effects evalQualifiedImport :: ( AbstractValue address value effects
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Env address) effects
, Member (Modules address value) effects , Member (Modules address value) effects
, Member (Reader (Environment address)) effects
, Member (State (Environment address)) effects
, Member (State (Exports address)) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Ord address , Ord address
, Reducer.Reducer value (Cell address value) , Reducer.Reducer value (Cell address value)
) )
=> Name -> ModulePath -> Evaluator address value effects value => Name -> ModulePath -> Evaluator address value effects value
evalQualifiedImport name path = letrec' name $ \addr -> do evalQualifiedImport name path = letrec' name $ \addr -> do
importedEnv <- maybe emptyEnv fst <$> isolate (require path) importedEnv <- maybe emptyEnv fst <$> require path
bindAll importedEnv bindAll importedEnv
unit <$ makeNamespace name addr Nothing unit <$ makeNamespace name addr Nothing
@ -163,7 +161,7 @@ instance Evaluatable QualifiedImport where
go ((name, path) :| []) = evalQualifiedImport name path go ((name, path) :| []) = evalQualifiedImport name path
-- Evaluate each parent module, just creating a namespace -- Evaluate each parent module, just creating a namespace
go ((name, path) :| xs) = letrec' name $ \addr -> do go ((name, path) :| xs) = letrec' name $ \addr -> do
void $ isolate (require path) void $ require path
void $ go (NonEmpty.fromList xs) void $ go (NonEmpty.fromList xs)
makeNamespace name addr Nothing makeNamespace name addr Nothing
@ -182,13 +180,13 @@ instance Evaluatable QualifiedAliasedImport where
modulePaths <- resolvePythonModules name modulePaths <- resolvePythonModules name
-- Evaluate each parent module -- Evaluate each parent module
for_ (NonEmpty.init modulePaths) (isolate . require) for_ (NonEmpty.init modulePaths) require
-- Evaluate and import the last module, aliasing and updating the environment -- Evaluate and import the last module, aliasing and updating the environment
alias <- either (throwEvalError . FreeVariablesError) pure (freeVariable $ subterm aliasTerm) alias <- either (throwEvalError . FreeVariablesError) pure (freeVariable $ subterm aliasTerm)
Rval <$> letrec' alias (\addr -> do Rval <$> letrec' alias (\addr -> do
let path = NonEmpty.last modulePaths let path = NonEmpty.last modulePaths
importedEnv <- maybe emptyEnv fst <$> isolate (require path) importedEnv <- maybe emptyEnv fst <$> require path
bindAll importedEnv bindAll importedEnv
unit <$ makeNamespace alias addr Nothing) unit <$ makeNamespace alias addr Nothing)

View File

@ -26,7 +26,7 @@ resolveRubyName name = do
let name' = cleanNameOrPath name let name' = cleanNameOrPath name
let paths = [name' <.> "rb"] let paths = [name' <.> "rb"]
modulePath <- resolve paths modulePath <- resolve paths
maybe (throwResumable $ NotFoundError name' paths Language.Ruby) pure modulePath maybeM (throwResumable $ NotFoundError name' paths Language.Ruby) modulePath
-- load "/root/src/file.rb" -- load "/root/src/file.rb"
resolveRubyPath :: ( Member (Modules address value) effects resolveRubyPath :: ( Member (Modules address value) effects
@ -37,7 +37,7 @@ resolveRubyPath :: ( Member (Modules address value) effects
resolveRubyPath path = do resolveRubyPath path = do
let name' = cleanNameOrPath path let name' = cleanNameOrPath path
modulePath <- resolve [name'] modulePath <- resolve [name']
maybe (throwResumable $ NotFoundError name' [name'] Language.Ruby) pure modulePath maybeM (throwResumable $ NotFoundError name' [name'] Language.Ruby) modulePath
cleanNameOrPath :: ByteString -> String cleanNameOrPath :: ByteString -> String
cleanNameOrPath = BC.unpack . dropRelativePrefix . stripQuotes cleanNameOrPath = BC.unpack . dropRelativePrefix . stripQuotes
@ -73,7 +73,7 @@ instance Evaluatable Require where
name <- subtermValue x >>= asString name <- subtermValue x >>= asString
path <- resolveRubyName name path <- resolveRubyName name
traceResolve name path traceResolve name path
(importedEnv, v) <- isolate (doRequire path) (importedEnv, v) <- doRequire path
bindAll importedEnv bindAll importedEnv
pure (Rval 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 pure (Rval 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
@ -109,10 +109,9 @@ instance Evaluatable Load where
eval (Load _) = raiseEff (fail "invalid argument supplied to load, path is required") eval (Load _) = raiseEff (fail "invalid argument supplied to load, path is required")
doLoad :: ( AbstractValue address value effects doLoad :: ( AbstractValue address value effects
, Member (Env address) effects
, Member (Modules address value) effects , Member (Modules address value) effects
, Member (Resumable ResolutionError) effects , Member (Resumable ResolutionError) effects
, Member (State (Environment address)) effects
, Member (State (Exports address)) effects
, Member Trace effects , Member Trace effects
) )
=> ByteString => ByteString
@ -121,7 +120,7 @@ doLoad :: ( AbstractValue address value effects
doLoad path shouldWrap = do doLoad path shouldWrap = do
path' <- resolveRubyPath path path' <- resolveRubyPath path
traceResolve path path' traceResolve path path'
importedEnv <- maybe emptyEnv fst <$> isolate (load path') importedEnv <- maybe emptyEnv fst <$> load path'
unless shouldWrap $ bindAll importedEnv unless shouldWrap $ bindAll importedEnv
pure (boolean Prelude.True) -- load always returns true. http://ruby-doc.org/core-2.5.0/Kernel.html#method-i-load pure (boolean Prelude.True) -- load always returns true. http://ruby-doc.org/core-2.5.0/Kernel.html#method-i-load

View File

@ -135,10 +135,8 @@ javascriptExtensions = ["js"]
evalRequire :: ( AbstractValue address value effects evalRequire :: ( AbstractValue address value effects
, Member (Allocator address value) effects , Member (Allocator address value) effects
, Member (Env address) effects
, Member (Modules address value) effects , Member (Modules address value) effects
, Member (Reader (Environment address)) effects
, Member (State (Environment address)) effects
, Member (State (Exports address)) effects
, Member (State (Heap address (Cell address) value)) effects , Member (State (Heap address (Cell address) value)) effects
, Ord address , Ord address
, Reducer value (Cell address value) , Reducer value (Cell address value)
@ -147,7 +145,7 @@ evalRequire :: ( AbstractValue address value effects
-> Name -> Name
-> Evaluator address value effects value -> Evaluator address value effects value
evalRequire modulePath alias = letrec' alias $ \addr -> do evalRequire modulePath alias = letrec' alias $ \addr -> do
importedEnv <- maybe emptyEnv fst <$> isolate (require modulePath) importedEnv <- maybe emptyEnv fst <$> require modulePath
bindAll importedEnv bindAll importedEnv
unit <$ makeNamespace alias addr Nothing unit <$ makeNamespace alias addr Nothing
@ -164,7 +162,7 @@ instance Show1 Import where liftShowsPrec = genericLiftShowsPrec
instance Evaluatable Import where instance Evaluatable Import where
eval (Import symbols importPath) = do eval (Import symbols importPath) = do
modulePath <- resolveWithNodejsStrategy importPath typescriptExtensions modulePath <- resolveWithNodejsStrategy importPath typescriptExtensions
importedEnv <- maybe emptyEnv fst <$> isolate (require modulePath) importedEnv <- maybe emptyEnv fst <$> require modulePath
bindAll (renamed importedEnv) $> Rval unit bindAll (renamed importedEnv) $> Rval unit
where where
renamed importedEnv renamed importedEnv
@ -214,7 +212,7 @@ instance ToJSONFields1 SideEffectImport
instance Evaluatable SideEffectImport where instance Evaluatable SideEffectImport where
eval (SideEffectImport importPath) = do eval (SideEffectImport importPath) = do
modulePath <- resolveWithNodejsStrategy importPath typescriptExtensions modulePath <- resolveWithNodejsStrategy importPath typescriptExtensions
void $ isolate (require modulePath) void $ require modulePath
pure (Rval unit) pure (Rval unit)
@ -232,7 +230,7 @@ instance Evaluatable QualifiedExport where
eval (QualifiedExport exportSymbols) = do eval (QualifiedExport exportSymbols) = do
-- Insert the aliases with no addresses. -- Insert the aliases with no addresses.
for_ exportSymbols $ \(name, alias) -> for_ exportSymbols $ \(name, alias) ->
addExport name alias Nothing export name alias Nothing
pure (Rval unit) pure (Rval unit)
@ -249,11 +247,11 @@ instance ToJSONFields1 QualifiedExportFrom
instance Evaluatable QualifiedExportFrom where instance Evaluatable QualifiedExportFrom where
eval (QualifiedExportFrom importPath exportSymbols) = do eval (QualifiedExportFrom importPath exportSymbols) = do
modulePath <- resolveWithNodejsStrategy importPath typescriptExtensions modulePath <- resolveWithNodejsStrategy importPath typescriptExtensions
importedEnv <- maybe emptyEnv fst <$> isolate (require modulePath) importedEnv <- maybe emptyEnv fst <$> require modulePath
-- Look up addresses in importedEnv and insert the aliases with addresses into the exports. -- Look up addresses in importedEnv and insert the aliases with addresses into the exports.
for_ exportSymbols $ \(name, alias) -> do for_ exportSymbols $ \(name, alias) -> do
let address = Env.lookup name importedEnv let address = Env.lookup name importedEnv
maybe (throwEvalError $ ExportError modulePath name) (addExport name alias . Just) address maybe (throwEvalError $ ExportError modulePath name) (export name alias . Just) address
pure (Rval unit) pure (Rval unit)
newtype DefaultExport a = DefaultExport { defaultExport :: a } newtype DefaultExport a = DefaultExport { defaultExport :: a }
@ -272,8 +270,8 @@ instance Evaluatable DefaultExport where
Just name -> do Just name -> do
addr <- lookupOrAlloc name addr <- lookupOrAlloc name
assign addr v assign addr v
addExport name name Nothing export name name Nothing
void $ bind name addr bind name addr
Nothing -> throwEvalError DefaultExportError Nothing -> throwEvalError DefaultExportError
pure (Rval unit) pure (Rval unit)

View File

@ -129,7 +129,7 @@ resumingAddressError = runAddressErrorWith (\ err -> trace ("AddressError:" <> s
UnallocatedAddress _ -> pure lowerBound UnallocatedAddress _ -> pure lowerBound
UninitializedAddress _ -> pure hole) UninitializedAddress _ -> pure hole)
resumingValueError :: (Member (State (Environment address)) effects, Member Trace effects, Show address) => Evaluator address (Value address body) (Resumable (ValueError address body) ': effects) a -> Evaluator address (Value address body) effects a resumingValueError :: (Member Trace effects, Show address) => Evaluator address (Value address body) (Resumable (ValueError address body) ': effects) a -> Evaluator address (Value address body) effects a
resumingValueError = runValueErrorWith (\ err -> trace ("ValueError" <> show err) *> case err of resumingValueError = runValueErrorWith (\ err -> trace ("ValueError" <> show err) *> case err of
CallError val -> pure val CallError val -> pure val
StringError val -> pure (pack (show val)) StringError val -> pure (pack (show val))
@ -139,7 +139,7 @@ resumingValueError = runValueErrorWith (\ err -> trace ("ValueError" <> show err
NumericError{} -> pure hole NumericError{} -> pure hole
Numeric2Error{} -> pure hole Numeric2Error{} -> pure hole
ComparisonError{} -> pure hole ComparisonError{} -> pure hole
NamespaceError{} -> getEnv NamespaceError{} -> pure emptyEnv
BitwiseError{} -> pure hole BitwiseError{} -> pure hole
Bitwise2Error{} -> pure hole Bitwise2Error{} -> pure hole
KeyValueError{} -> pure (hole, hole) KeyValueError{} -> pure (hole, hole)

View File

@ -96,7 +96,7 @@ readBlobsFromHandle = fmap toBlobs . readFromHandle
readBlobFromPath :: MonadIO m => File -> m Blob.Blob readBlobFromPath :: MonadIO m => File -> m Blob.Blob
readBlobFromPath file = do readBlobFromPath file = do
maybeFile <- readFile file maybeFile <- readFile file
maybe (fail ("cannot read '" <> show file <> "', file not found or language not supported.")) pure maybeFile maybeM (fail ("cannot read '" <> show file <> "', file not found or language not supported.")) maybeFile
readProjectFromPaths :: MonadIO m => Maybe FilePath -> FilePath -> Language -> [FilePath] -> m Project readProjectFromPaths :: MonadIO m => Maybe FilePath -> FilePath -> Language -> [FilePath] -> m Project
readProjectFromPaths maybeRoot path lang excludeDirs = do readProjectFromPaths maybeRoot path lang excludeDirs = do

View File

@ -11,16 +11,16 @@ spec :: Spec
spec = parallel $ do spec = parallel $ do
describe "evaluates Go" $ do describe "evaluates Go" $ do
it "imports and wildcard imports" $ do it "imports and wildcard imports" $ do
((_, state), _) <- evaluate "main.go" ((Right [(_, env)], state), _) <- evaluate "main.go"
Env.names (environment state) `shouldBe` [ "Bar", "Rab", "foo", "main" ] Env.names env `shouldBe` [ "Bar", "Rab", "foo", "main" ]
(derefQName (heap state) ("foo" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("foo", ["New"]) (derefQName (heap state) ("foo" :| []) env >>= deNamespace) `shouldBe` Just ("foo", ["New"])
it "imports with aliases (and side effects only)" $ do it "imports with aliases (and side effects only)" $ do
((_, state), _) <- evaluate "main1.go" ((Right [(_, env)], state), _) <- evaluate "main1.go"
Env.names (environment state) `shouldBe` [ "f", "main" ] Env.names env `shouldBe` [ "f", "main" ]
(derefQName (heap state) ("f" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("f", ["New"]) (derefQName (heap state) ("f" :| []) env >>= deNamespace) `shouldBe` Just ("f", ["New"])
where where
fixtures = "test/fixtures/go/analysis/" fixtures = "test/fixtures/go/analysis/"

View File

@ -12,22 +12,22 @@ spec :: Spec
spec = parallel $ do spec = parallel $ do
describe "PHP" $ do describe "PHP" $ do
it "evaluates include and require" $ do it "evaluates include and require" $ do
((res, state), _) <- evaluate "main.php" ((Right [(res, env)], state), _) <- evaluate "main.php"
res `shouldBe` Right [unit] res `shouldBe` unit
Env.names (environment state) `shouldBe` [ "bar", "foo" ] Env.names env `shouldBe` [ "bar", "foo" ]
it "evaluates include_once and require_once" $ do it "evaluates include_once and require_once" $ do
((res, state), _) <- evaluate "main_once.php" ((Right [(res, env)], state), _) <- evaluate "main_once.php"
res `shouldBe` Right [unit] res `shouldBe` unit
Env.names (environment state) `shouldBe` [ "bar", "foo" ] Env.names env `shouldBe` [ "bar", "foo" ]
it "evaluates namespaces" $ do it "evaluates namespaces" $ do
((_, state), _) <- evaluate "namespaces.php" ((Right [(_, env)], state), _) <- evaluate "namespaces.php"
Env.names (environment state) `shouldBe` [ "Foo", "NS1" ] Env.names env `shouldBe` [ "Foo", "NS1" ]
(derefQName (heap state) ("NS1" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("NS1", ["Sub1", "b", "c"]) (derefQName (heap state) ("NS1" :| []) env >>= deNamespace) `shouldBe` Just ("NS1", ["Sub1", "b", "c"])
(derefQName (heap state) ("NS1" :| ["Sub1"]) (environment state) >>= deNamespace) `shouldBe` Just ("Sub1", ["Sub2"]) (derefQName (heap state) ("NS1" :| ["Sub1"]) env >>= deNamespace) `shouldBe` Just ("Sub1", ["Sub2"])
(derefQName (heap state) ("NS1" :| ["Sub1", "Sub2"]) (environment state) >>= deNamespace) `shouldBe` Just ("Sub2", ["f"]) (derefQName (heap state) ("NS1" :| ["Sub1", "Sub2"]) env >>= deNamespace) `shouldBe` Just ("Sub2", ["f"])
where where
fixtures = "test/fixtures/php/analysis/" fixtures = "test/fixtures/php/analysis/"

View File

@ -14,33 +14,33 @@ spec :: Spec
spec = parallel $ do spec = parallel $ do
describe "evaluates Python" $ do describe "evaluates Python" $ do
it "imports" $ do it "imports" $ do
((_, state), _) <- evaluate "main.py" ((Right [(_, env)], state), _) <- evaluate "main.py"
Env.names (environment state) `shouldContain` [ "a", "b" ] Env.names env `shouldContain` [ "a", "b" ]
(derefQName (heap state) ("a" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("a", ["foo"]) (derefQName (heap state) ("a" :| []) env >>= deNamespace) `shouldBe` Just ("a", ["foo"])
(derefQName (heap state) ("b" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("b", ["c"]) (derefQName (heap state) ("b" :| []) env >>= deNamespace) `shouldBe` Just ("b", ["c"])
(derefQName (heap state) ("b" :| ["c"]) (environment state) >>= deNamespace) `shouldBe` Just ("c", ["baz"]) (derefQName (heap state) ("b" :| ["c"]) env >>= deNamespace) `shouldBe` Just ("c", ["baz"])
it "imports with aliases" $ do it "imports with aliases" $ do
env <- environment . snd . fst <$> evaluate "main1.py" ((Right [(_, env)], _), _) <- evaluate "main1.py"
Env.names env `shouldContain` [ "b", "e" ] Env.names env `shouldContain` [ "b", "e" ]
it "imports using 'from' syntax" $ do it "imports using 'from' syntax" $ do
env <- environment . snd . fst <$> evaluate "main2.py" ((Right [(_, env)], _), _) <- evaluate "main2.py"
Env.names env `shouldContain` [ "bar", "foo" ] Env.names env `shouldContain` [ "bar", "foo" ]
it "imports with relative syntax" $ do it "imports with relative syntax" $ do
((_, state), _) <- evaluate "main3.py" ((Right [(_, env)], state), _) <- evaluate "main3.py"
Env.names (environment state) `shouldContain` [ "utils" ] Env.names env `shouldContain` [ "utils" ]
(derefQName (heap state) ("utils" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("utils", ["to_s"]) (derefQName (heap state) ("utils" :| []) env >>= deNamespace) `shouldBe` Just ("utils", ["to_s"])
it "subclasses" $ do it "subclasses" $ do
((res, _), _) <- evaluate "subclass.py" ((res, _), _) <- evaluate "subclass.py"
res `shouldBe` Right [String "\"bar\""] fmap fst <$> res `shouldBe` Right [String "\"bar\""]
it "handles multiple inheritance left-to-right" $ do it "handles multiple inheritance left-to-right" $ do
((res, _), _) <- evaluate "multiple_inheritance.py" ((res, _), _) <- evaluate "multiple_inheritance.py"
res `shouldBe` Right [String "\"foo!\""] fmap fst <$> res `shouldBe` Right [String "\"foo!\""]
where where
ns n = Just . Latest . Last . Just . Namespace n ns n = Just . Latest . Last . Just . Namespace n

View File

@ -20,58 +20,57 @@ spec :: Spec
spec = parallel $ do spec = parallel $ do
describe "Ruby" $ do describe "Ruby" $ do
it "evaluates require_relative" $ do it "evaluates require_relative" $ do
((res, state), _) <- evaluate "main.rb" ((Right [(res, env)], state), _) <- evaluate "main.rb"
res `shouldBe` Right [Value.Integer (Number.Integer 1)] res `shouldBe` Value.Integer (Number.Integer 1)
Env.names (environment state) `shouldContain` ["foo"] Env.names env `shouldContain` ["foo"]
it "evaluates load" $ do it "evaluates load" $ do
env <- environment . snd . fst <$> evaluate "load.rb" ((Right [(_, env)], _), _) <- evaluate "load.rb"
Env.names env `shouldContain` ["foo"] Env.names env `shouldContain` ["foo"]
it "evaluates load with wrapper" $ do it "evaluates load with wrapper" $ do
((res, state), _) <- evaluate "load-wrap.rb" ((res, state), _) <- evaluate "load-wrap.rb"
res `shouldBe` Left (SomeExc (inject @(EnvironmentError Precise) (FreeVariable "foo"))) res `shouldBe` Left (SomeExc (inject @(EnvironmentError Precise) (FreeVariable "foo")))
Env.names (environment state) `shouldContain` [ "Object" ]
it "evaluates subclass" $ do it "evaluates subclass" $ do
((res, state), _) <- evaluate "subclass.rb" ((Right [(res, env)], state), _) <- evaluate "subclass.rb"
res `shouldBe` Right [String "\"<bar>\""] res `shouldBe` String "\"<bar>\""
Env.names (environment state) `shouldContain` [ "Bar", "Foo" ] Env.names env `shouldContain` [ "Bar", "Foo" ]
(derefQName (heap state) ("Bar" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("Bar", ["baz", "foo", "inspect"]) (derefQName (heap state) ("Bar" :| []) env >>= deNamespace) `shouldBe` Just ("Bar", ["baz", "foo", "inspect"])
it "evaluates modules" $ do it "evaluates modules" $ do
((res, state), _) <- evaluate "modules.rb" ((Right [(res, env)], state), _) <- evaluate "modules.rb"
res `shouldBe` Right [String "\"<hello>\""] res `shouldBe` String "\"<hello>\""
Env.names (environment state) `shouldContain` [ "Bar" ] Env.names env `shouldContain` [ "Bar" ]
it "handles break correctly" $ do it "handles break correctly" $ do
((res, _), _) <- evaluate "break.rb" ((res, _), _) <- evaluate "break.rb"
res `shouldBe` Right [Value.Integer (Number.Integer 3)] fmap fst <$> res `shouldBe` Right [Value.Integer (Number.Integer 3)]
it "handles break correctly" $ do it "handles break correctly" $ do
((res, _), _) <- evaluate "next.rb" ((res, _), _) <- evaluate "next.rb"
res `shouldBe` Right [Value.Integer (Number.Integer 8)] fmap fst <$> res `shouldBe` Right [Value.Integer (Number.Integer 8)]
it "calls functions with arguments" $ do it "calls functions with arguments" $ do
((res, _), _) <- evaluate "call.rb" ((res, _), _) <- evaluate "call.rb"
res `shouldBe` Right [Value.Integer (Number.Integer 579)] fmap fst <$> res `shouldBe` Right [Value.Integer (Number.Integer 579)]
it "evaluates early return statements" $ do it "evaluates early return statements" $ do
((res, _), _) <- evaluate "early-return.rb" ((res, _), _) <- evaluate "early-return.rb"
res `shouldBe` Right [Value.Integer (Number.Integer 123)] fmap fst <$> res `shouldBe` Right [Value.Integer (Number.Integer 123)]
it "has prelude" $ do it "has prelude" $ do
((res, _), _) <- evaluate "preluded.rb" ((res, _), _) <- evaluate "preluded.rb"
res `shouldBe` Right [String "\"<foo>\""] fmap fst <$> res `shouldBe` Right [String "\"<foo>\""]
it "evaluates __LINE__" $ do it "evaluates __LINE__" $ do
((res, _), _) <- evaluate "line.rb" ((res, _), _) <- evaluate "line.rb"
res `shouldBe` Right [Value.Integer (Number.Integer 4)] fmap fst <$> res `shouldBe` Right [Value.Integer (Number.Integer 4)]
it "resolves builtins used in the prelude" $ do it "resolves builtins used in the prelude" $ do
((res, _), traces) <- evaluate "puts.rb" ((res, _), traces) <- evaluate "puts.rb"
res `shouldBe` Right [Unit] fmap fst <$> res `shouldBe` Right [Unit]
traces `shouldContain` [ "\"hello\"" ] traces `shouldContain` [ "\"hello\"" ]
where where

View File

@ -15,19 +15,19 @@ spec :: Spec
spec = parallel $ do spec = parallel $ do
describe "evaluates TypeScript" $ do describe "evaluates TypeScript" $ do
it "imports with aliased symbols" $ do it "imports with aliased symbols" $ do
env <- environment . snd . fst <$> evaluate "main.ts" ((Right [(_, env)], _), _) <- evaluate "main.ts"
Env.names env `shouldBe` [ "bar", "quz" ] Env.names env `shouldBe` [ "bar", "quz" ]
it "imports with qualified names" $ do it "imports with qualified names" $ do
((_, state), _) <- evaluate "main1.ts" ((Right [(_, env)], state), _) <- evaluate "main1.ts"
Env.names (environment state) `shouldBe` [ "b", "z" ] Env.names env `shouldBe` [ "b", "z" ]
(derefQName (heap state) ("b" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("b", [ "baz", "foo" ]) (derefQName (heap state) ("b" :| []) env >>= deNamespace) `shouldBe` Just ("b", [ "baz", "foo" ])
(derefQName (heap state) ("z" :| []) (environment state) >>= deNamespace) `shouldBe` Just ("z", [ "baz", "foo" ]) (derefQName (heap state) ("z" :| []) env >>= deNamespace) `shouldBe` Just ("z", [ "baz", "foo" ])
it "side effect only imports" $ do it "side effect only imports" $ do
env <- environment . snd . fst <$> evaluate "main2.ts" ((res, _), _) <- evaluate "main2.ts"
env `shouldBe` emptyEnv fmap snd <$> res `shouldBe` Right [emptyEnv]
it "fails exporting symbols not defined in the module" $ do it "fails exporting symbols not defined in the module" $ do
((res, _), _) <- evaluate "bad-export.ts" ((res, _), _) <- evaluate "bad-export.ts"
@ -35,7 +35,7 @@ spec = parallel $ do
it "evaluates early return statements" $ do it "evaluates early return statements" $ do
((res, _), _) <- evaluate "early-return.ts" ((res, _), _) <- evaluate "early-return.ts"
res `shouldBe` Right [Value.Float (Number.Decimal 123.0)] fmap fst <$> res `shouldBe` Right [Value.Float (Number.Decimal 123.0)]
where where
fixtures = "test/fixtures/typescript/analysis/" fixtures = "test/fixtures/typescript/analysis/"

View File

@ -20,13 +20,13 @@ spec :: Spec
spec = parallel $ do spec = parallel $ do
it "constructs integers" $ do it "constructs integers" $ do
(expected, _) <- evaluate (pure (integer 123)) (expected, _) <- evaluate (pure (integer 123))
expected `shouldBe` Right (Value.Integer (Number.Integer 123)) fst <$> expected `shouldBe` Right (Value.Integer (Number.Integer 123))
it "calls functions" $ do it "calls functions" $ do
(expected, _) <- evaluate $ do (expected, _) <- evaluate $ do
identity <- closure [name "x"] lowerBound (variable (name "x")) identity <- closure [name "x"] lowerBound (variable (name "x"))
call identity [pure (integer 123)] call identity [pure (integer 123)]
expected `shouldBe` Right (Value.Integer (Number.Integer 123)) fst <$> expected `shouldBe` Right (Value.Integer (Number.Integer 123))
evaluate evaluate
= runM = runM
@ -38,6 +38,7 @@ evaluate
. runEnvironmentError . runEnvironmentError
. runAddressError . runAddressError
. runAllocator . runAllocator
. runEnv lowerBound
. runReturn . runReturn
. runLoopControl . runLoopControl

View File

@ -34,7 +34,6 @@ import Data.Project as X
import Data.Functor.Listable as X import Data.Functor.Listable as X
import Data.Language as X import Data.Language as X
import Data.List.NonEmpty as X (NonEmpty(..)) import Data.List.NonEmpty as X (NonEmpty(..))
import Data.Monoid as X (Last(..))
import Data.Range as X import Data.Range as X
import Data.Record as X import Data.Record as X
import Data.Source as X import Data.Source as X