Pugs-6.2.13.1: src/Pugs/Eval/Var.hs
{-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances -funbox-strict-fields -fparr #-}
module Pugs.Eval.Var (
findVar, findVarRef, findSub,
inferExpType, inferExpCxt, FindSubFailure(..),
packageOf, toPackage, toQualified,
) where
import qualified Data.Map as Map
import qualified StringTable.AtomMap as AtomMap
import Pugs.Internals
import Pugs.AST
import Pugs.Types
import Pugs.Bind
import Pugs.Prim.List (op2Reduce, op1HyperPrefix, op1HyperPostfix, op2Hyper)
import Pugs.Prim.Param (foldParam)
import Pugs.Pretty
import Pugs.Config
import Pugs.Monads
import Pugs.Class hiding (Val)
import qualified Pugs.Val as Val
import qualified Data.ByteString.Char8 as Buf
findVar :: Var -> Eval (Maybe VRef)
findVar var
| SType <- v_sigil var
, not (isGlobalVar var)
= return Nothing
| otherwise = do
rv <- findVarRef var
case rv of
Just ref -> fmap Just (readPadEntry ref)
Nothing -> return Nothing
constPadEntry :: VRef -> PadEntry
constPadEntry r = PEConstant{ pe_type = refType r, pe_proto = r, pe_flags = mempty }
lookupShellEnvironment :: ByteString -> Eval (Maybe PadEntry)
lookupShellEnvironment name = do
exists <- evalExp $ App (_Var "&exists") (Just (_Var "%*ENV")) [Val (VStr $ cast name)]
case exists of
VBool False -> do
die "no such ENV variable" name
_ -> do
rv <- enterLValue (evalExp $ Syn "{}" [_Var "%*ENV", Val (VStr $ cast name)])
ref <- fromVal rv
return (Just (constPadEntry ref))
findVarRef :: Var -> Eval (Maybe PadEntry)
findVarRef var@MkVar{ v_sigil = sig, v_twigil = twi, v_name = name, v_package = pkg }
| Just var' <- dropVarPkg (__"CALLER") var = do
maybeCaller <- asks envCaller
case maybeCaller of
Just env -> local (const env) $ findVarRef var'
Nothing -> die "cannot access CALLER:: in top level" var
| Just var' <- dropVarPkg (__"ENV") var = fix $ \upLevel -> do
maybeCaller <- asks envCaller
case maybeCaller of
Just env -> local (const env) $ do
rv <- findVarRef var'
if isJust rv then return rv else upLevel
-- final callback: try an "environment" lookup
-- XXX: how does "@+PATH" differ from "$+PATH"?
-- XXX: how to tell empty env from nonexistent env?
-- should we allow writes?
Nothing -> lookupShellEnvironment (cast name)
| Just var' <- dropVarPkg (__"OUTER") var = ($ var') . fix $ \outerLevel v -> do
mpads <- asks envLexPads
case mpads of
(_:outers@(outer:_)) -> local (\env -> env{ envLexPads = outers }) $ do
case dropVarPkg (__"OUTER") v of
Just v' -> outerLevel v'
_ -> do
pad <- case outer of
PRuntime p -> return p
PCompiling p-> readMPad p
return (lookupPad v pad)
_ -> die "cannot access OUTER:: in top level" name
| pkg /= emptyPkg = doFindVarRef var
| TMagical <- twi = do
rv <- getMagical var
case rv of
Nothing -> doFindVarRef var
Just val -> return (Just (constPadEntry (MkRef . constScalar $ val)))
| SHash <- sig, nullID == name = do
{- %CALLER::, %OUTER::, %Package::, etc, all recurse to here. -}
pad <- asks envLexical
let plist = padToList pad
hlist <- mapM padEntryToHashEntry plist
let hash = IHash $ Map.fromList hlist
return $ Just (constPadEntry $ MkRef hash)
| otherwise = doFindVarRef var
where
padEntryToHashEntry :: (Var, PadEntry) -> Eval (VStr, Val)
padEntryToHashEntry (key, entry) = do
vref <- readPadEntry entry
let val = VRef vref
return (cast key, val)
doFindVarRef :: Var -> Eval (Maybe PadEntry)
doFindVarRef var = do
lexSym <- fmap (lookupPad var . envLexical) ask
if isJust lexSym then return lexSym else do
-- XXX - this is bogus; we should not fallback if it's not in lex scope.
glob <- readMPad . envGlobal =<< ask
var' <- toQualified var
let globSym = lookupPad var' glob
if isJust globSym then return globSym else do
-- XXX - ditto for globals
let globSym = lookupPad (toGlobalVar var) glob
if isJust globSym then return globSym else do
return Nothing
{-|
The findSub dispatch system:
The Eval.Var findSub dispatch system was written before S12's
dispatch order was specced clearly. Back then there were no clear
distinction between single and multiple dispatch, and so both were
lumped in a single findSub loop. So "$x.foo.bar" is no different
from bar(foo($x)), rather, bar(foo($x:):). But MMD dictates that
you have to find which of the candidate &bar to be dispatched to
before you fully evaluate its argument foo($x:) under its specified
context. But you can't find out which candidates of &bar to
dispatch to, unless you know the type of foo($x). That's a
chicken-egg problem. So I wrote a tiny type inferencer to guess a
type of an Exp without actually evaluating its side effects. The
idea is that foo($x) is first inferred, then uses that inferred type
to decide which bar() to call, and finally evaluate foo($x) in full.
Using the argument type expected by bar(). But the infer engine
didn't handle indexed expressions well. $x[0] etc. In any case,
that tiny inferencer is obsolete under the new (November 2005) S12
and docs/notes/multimethods.pod. Which would be implemented at PIL
layer. The inferencer fix I just committed (r8874) for indexed
expressions, just checks if both the indice and indexee are simple
expressions (i.e. things that can be evaluated without side
effects), and if so, it just evaluates them to find out their actual
type.
-}
data FindSubFailure
= NoMatchingMulti
| NoSuchSub
| NoSuchMethod !Type
deriving (Show)
_SUPER :: ByteString
_SUPER = __"SUPER"
_NEXT :: ByteString
_NEXT = __"NEXT"
-- This no longer handles multi dispatch now. Yay!
findSub :: Var -- ^ Name, with leading @\&@.
-> Maybe Exp -- ^ Invocant
-> [Exp] -- ^ Other arguments
-> Eval (Either FindSubFailure VCode)
findSub _var _invs _args
| Nothing <- _invs = do
findBuiltinSub NoMatchingMulti _var
| not (isQualifiedVar _var) = do
case unwrap _inv of
Val vv@VV{} -> withExternalCall callMethodVV vv
Val sv@PerlSV{} -> withExternalCall callMethodVV sv
inv' -> do
typ <- evalInvType inv'
if typ == mkType "Scalar::Perl5" -- code for "VV"
then evalExp inv' >>= withExternalCall callMethodVV
else findTypedSub (cast typ) _var
| Just var' <- dropVarPkg _SUPER _var = do
pkg <- asks envPackage
findSuperSub pkg var'
| Just var' <- dropVarPkg _NEXT _var = do
typ <- evalInvType _inv
findSuperSub (cast typ) var'
| otherwise = do
findBuiltinSub NoMatchingMulti _var
where
_inv = fromJust _invs
-- findSuperSub :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => Pkg -> Var -> Eval (Either FindSubFailure VCode)
findSuperSub pkg var = do
subs <- findWithSuper pkg var
subs' <- either (flip findBuiltinSub var) (return . Right) subs
case subs' of
-- Recursion prevention -- SUPER::foo should not go back to ThisClas::foo
Right sub | cast (Buf.cons '&' $ subName sub) == var{ v_package = pkg } -> do
return (Left . NoSuchMethod $ cast pkg)
_ -> do
return subs'
-- findTypedSub :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => Pkg -> Var -> Eval (Either FindSubFailure VCode)
findTypedSub pkg var = do
subs <- findWithPkg pkg var
either (flip findBuiltinSub var) (return . Right) subs
evalInvType :: Exp -> Eval Type
evalInvType x = inferExpType x
withExternalCall callMeth inv = do
fmap (err . NoSuchMethod $ valType inv) $ do
metaSub <- possiblyBuildMetaopVCode _var
if isJust metaSub then return metaSub else callMeth
-- callMethodVV :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => Eval (Maybe VCode)
callMethodVV = do
let methName = cast (v_name _var)
-- Look up the proto for the method in VV land right here
-- Whether it matched or not, it's the proto's signature
-- that's available to the inferencer, not any of its children's
-- (this is because MMD in newland is performed _after_ everything
-- has been reduced.)
return . Just $ mkPrim
{ subName = methName
, subParams = makeParams ["Object", "List", "Named"]
, subReturns = mkType "Any"
, subBody = Prim $ \(inv:named:pos:_) -> do
invVV <- fromVal inv :: Eval Val.Val
posVVs <- fromVals pos :: Eval [Val.Val]
namVVs <- do
list <- fromVal named
fmap AtomMap.fromList $ forM list $ \(k, v) -> do
key <- fromVal k
val <- fromVal v
return (key, val) :: Eval (ID, Val.Val)
rv <- tryT $ VV invVV ./ (methName, posVVs, namVVs)
case rv of
VError (VStr s) _
| "Can't locate object method" `isPrefixOf` s || "Can't call method" `isPrefixOf` s -> do
let capt = mi_arguments (cast (methName, (invVV:posVVs), namVVs) :: Call)
rv' <- tryT . evalExp $ App (Var _var) Nothing [Syn "|" [Val (VV (mkVal capt))]]
case rv' of
VError (VStr s') _ | "No compatible subroutine found" `isPrefixOf` s' -> EvalT $ return (RException rv)
VError{} -> EvalT $ return (RException rv')
_ -> return rv'
VError{} -> EvalT $ return (RException rv)
_ -> return rv
}
-- callMethodPerl5 :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => Eval (Maybe VCode)
-- findWithPkg :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => Pkg -> Var -> Eval (Either FindSubFailure VCode)
findWithPkg pkg var = do
subs <- findSub' var{ v_package = pkg }
maybe (findWithSuper pkg var) (return . Right) subs
-- findWithSuper :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => Pkg -> Var -> Eval (Either FindSubFailure VCode)
findWithSuper pkg var = do
-- get superclasses
attrs <- fmap (fmap (filter (/= pkg) . nub)) $ findAttrs pkg
if isNothing attrs || null (fromJust attrs) then fmap (err NoMatchingMulti) (findSub' var) else do
-- XXX - "reverse" below is a crude hack before we have C3 dispatch;
-- - this is such that "class X is Object is Moose" can dispatch with Moose first.
(`fix` (reverse $ fromJust attrs)) $ \run pkgs -> do
if null pkgs then return (Left $ NoSuchMethod (cast pkg)) else do
subs <- findWithPkg (head pkgs) var
either (const $ run (tail pkgs)) (return . Right) subs
-- findSub' :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp]) => Var -> Eval (Maybe VCode)
findSub' var = do
subSyms <- findCodeSyms var
lens <- mapM argSlurpLen _invs_args
doFindSub lens subSyms
argSlurpLen :: Exp -> Eval Int
argSlurpLen (Val val) = valSlurpLen val
argSlurpLen (Var name) = do
val <- enterLValue $ evalExp (Var name)
valSlurpLen val
argSlurpLen (Syn "," list) = return $ length list
argSlurpLen (Syn "named" _) = return 0
argSlurpLen _ = return 1 -- XXX
valSlurpLen :: Val -> Eval Int
valSlurpLen (VList list) = return $ length list
valSlurpLen (VRef (MkRef (IArray av))) = array_fetchSize av
valSlurpLen (VRef (MkRef (IHash hv))) = hash_fetchSize hv
valSlurpLen _ = return 1 -- XXX
-- doFindSub :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => Int -> [(Var, Val)] -> Eval (Maybe VCode)
doFindSub slurpLens subSyms = do
subs' <- subs slurpLens subSyms
-- warn (unlines $ map (\(x, y) -> show (x, subParams y)) subs') ""
return $ case sort subs' of
((_, sub):_) -> Just sub
_ -> Nothing
_invs_args = map unwrap (maybe _args (:_args) _invs)
-- subs :: (_invs :: Maybe Exp, _args :: [Exp])
-- => Int -> [(Var, Val)] -> Eval [((Bool, Bool, Int, Int), VCode)]
subs slurpLens subSyms = fmap catMaybes . forM subSyms $ \sub@MkCode{ subReturns = ret } -> do
let (named, positional) = partition isNamedArg _invs_args
isNamedArg (Syn "named" _) = True
isNamedArg _ = False
rv = return $ arityMatch sub (length positional) (length named) slurpLens
maybeM rv $ \fun -> do
-- if deltaFromCxt ret == 0 then return Nothing else do
(deltaArgs, deltaCxt) <- case bindParams sub _invs _args of
Left{} -> return ([maxBound], 0)
Right s -> do
ds <- forM (subBindings s) $ \(prm, arg) -> case arg of
Syn "param-default" _ -> return Nothing
_ | isSlurpy prm -> return Nothing
_ -> do
argType <- inferExpType arg
return (Just $ deltaType (typeOfParam prm) argType)
cxt <- asks envContext
return (catMaybes ds, deltaType (typeOfCxt cxt) ret)
return ((isMulti sub, sum deltaArgs, -(length deltaArgs), deltaCxt), fun)
-- findBuiltinSub :: (_var :: Var, _invs :: Maybe Exp, _args :: [Exp])
-- => FindSubFailure -> Var -> Eval (Either FindSubFailure VCode)
findBuiltinSub failure var = do
subSyms <- findCodeSyms var
if null subSyms then (fmap (err NoSuchSub) (possiblyBuildMetaopVCode var)) else do
lens <- mapM argSlurpLen _invs_args
sub <- doFindSub lens subSyms
maybe (fmap (err failure) $ possiblyBuildMetaopVCode var) (return . Right) sub
-- firstArg :: (_args :: [Exp]) => [Exp]
firstArg = [maybe (Val undef) id (listToMaybe _args)]
firstTwoArgs
| [] <- _args = [Val undef, Val undef]
| [arg] <- _args = [arg, Val undef]
| otherwise = take 2 _args
metaPrim = mkPrim
{ subName = cast (v_name _var)
, subType = SubPrim
, subReturns = mkType "List"
}
buildPrefixHyper var = do
let rv = fmap (either (const Nothing) Just) $
findSub var Nothing firstArg
maybeM rv (return . makePrefixHyperCode)
makePrefixHyperCode code = metaPrim
{ subAssoc = subAssoc code
, subParams = subParams code
, subBody = Prim
(\x -> op1HyperPrefix code (listArg x))
}
buildPostfixHyper var = do
let rv = fmap (either (const Nothing) Just) $
findSub var Nothing firstArg
maybeM rv $ \code -> return $ metaPrim
{ subAssoc = subAssoc code
, subParams = subParams code
, subBody = Prim
(\x -> op1HyperPostfix code (listArg x))
}
buildInfixHyper var = do
let rv = fmap (either (const Nothing) Just) $
findSub var Nothing firstTwoArgs
maybeM rv (return . makeInfixHyperCode)
makeInfixHyperCode code = metaPrim
{ subAssoc = subAssoc code
, subParams = makeParams ["Any", "Any"]
, subBody = Prim (\[x, y] -> op2Hyper code x y)
}
buildReduce var foldOrScan nilOrHyper nilOrPost = do
let rv = fmap (either (const Nothing) Just) $
findSub var Nothing firstTwoArgs
maybeM rv $ \code -> return . maybePost $ metaPrim
{ subAssoc = ANil
, subParams = makeParams $
if any isLValue (subParams code)
then ["rw!List"] -- XXX - does not yet work for the [=] case
else ["List"]
, subReturns = anyType
, subBody = Prim $ \[vs] -> do
list_of_args <- fromVal vs
op2Reduce (foldOrScan == MScan) list_of_args . VCode $ case nilOrHyper of
MHyper -> makeInfixHyperCode code
_ -> code
}
where
maybePost
| MPost <- nilOrPost = makePrefixHyperCode
| otherwise = id
-- possiblyBuildMetaopVCode :: (_args :: [Exp]) => Var -> Eval (Maybe VCode)
possiblyBuildMetaopVCode var@MkVar{ v_meta = meta } = case meta of
MPost -> buildPrefixHyper var' -- +<<
MPre -> buildPostfixHyper var' -- >>+
MHyper -> buildInfixHyper var' -- >>+<<
MFold -> buildReduce varInfix MFold MNil MNil -- [+]
MScan -> buildReduce varInfix MScan MNil MNil -- [\+]
-- MFoldPost -> buildReduce varInfix MFold MNil MPost -- [+]
-- MScanPost -> buildReduce varInfix MScan MNil MPost -- [\+]
MHyperFold -> buildReduce varInfix MFold MHyper MNil -- [>>+<<]
MHyperScan -> buildReduce varInfix MScan MHyper MNil -- [>>+<<]
-- MHyperFoldPost -> buildReduce varInfix MFold MHyper MPost -- [>>+<<]
-- MHyperScanPost -> buildReduce varInfix MScan MHyper MPost -- [>>+<<]
_ -> return Nothing
where
var' = var{ v_meta = MNil }
varInfix = var{ v_meta = MNil, v_categ = C_infix }
metaVar :: Pkg -> Var
-- metaVar = MkVar SType TNil globalPkg CNil . cast
metaVar pkg = cast (':':'*':cast pkg)
{-
MkVar
{ v_sigil = SType
, v_twigil = TGlobal
, v_package = emptyPkg
, v_categ = CNil
, v_name = cast pkg
}
-}
err :: b -> Maybe a -> Either b a
err _ (Just j) = Right j
err x Nothing = Left x
listArg :: [Val] -> Val
listArg [x] = x
listArg xs = VList xs
makeParams :: [String] -> [Param]
makeParams = map (\p -> p{ isWritable = isLValue p }) . foldr foldParam [] . map takeWord
where
takeWord = takeWhile isWord . dropWhile (not . isWord)
isWord = not . (`elem` "(),:")
findAttrs :: Pkg -> Eval (Maybe [Pkg])
findAttrs pkg = do
maybeM (findVar $ metaVar pkg) $ \ref -> do
meta <- readRef ref
fetch <- doHash meta hash_fetchVal
fmap (map (cast :: String -> Pkg)) (fromVal =<< fetch "is")
{-|
Take an expression, and attempt to predict what type it will evaluate to
/without/ actually evaluating it.
-}
inferExpType :: Exp -> Eval Type
inferExpType exp@(Var var)
| TAttribute <- v_twigil var = fromVal =<< evalExp exp
| TPrivate <- v_twigil var = fromVal =<< evalExp exp
| otherwise = do
rv <- findVar var
case rv of
Nothing -> return $ typeOfSigilVar var
Just ref -> do
let typ = refType ref
if isaType "List" typ
then return typ
else fromVal =<< readRef ref
inferExpType (Val val) = fromVal val
inferExpType (App (Val val) _ _) = do
sub <- fromVal val
return $ subReturns sub
inferExpType (App (Var var) (Just inv) _)
| var == cast "&new"
= inferExpType $ unwrap inv
inferExpType (App (Var name) invs args) = do
sub <- findSub name invs args
return (either (const anyType) subReturns sub)
inferExpType (Ann (Cxt cxt) _) | typeOfCxt cxt /= (mkType "Any") = return $ typeOfCxt cxt
inferExpType (Ann _ exp) = inferExpType exp
inferExpType (Sym _ _ _ _ exp) = inferExpType exp
inferExpType (Stmts _ exp) = inferExpType exp
inferExpType (Syn "," _) = return $ mkType "List"
inferExpType (Syn "\\[]" _) = return $ mkType "Array"
inferExpType (Syn "\\{}" _) = return $ mkType "Hash"
inferExpType (Syn "&{}" _) = return $ mkType "Code"
inferExpType (Syn "@{}" _) = return $ mkType "Array"
inferExpType (Syn "%{}" _) = return $ mkType "Hash"
inferExpType (Syn "=>" _) = return $ mkType "Pair"
inferExpType (Syn "named" [_, exp]) = inferExpType exp
inferExpType (Syn "rx" _) = return $ mkType "Regex"
inferExpType (Syn "match" _)= return $ mkType "Match"
inferExpType (Syn "//" _) = return $ mkType "Regex" -- XXX Wrong
inferExpType exp@(Syn "{}" [_, idxExp]) = if isSimpleExp exp
then fromVal =<< enterRValue (evalExp exp)
else fmap typeOfCxt (inferExpCxt idxExp)
inferExpType exp@(Syn "[]" [_, idxExp]) = if isSimpleExp exp
then fromVal =<< enterRValue (evalExp exp)
else fmap typeOfCxt (inferExpCxt idxExp)
inferExpType (Syn "sub" [exp]) = inferExpType exp
inferExpType _ = return anyType
isSimpleExp :: Exp -> Bool
isSimpleExp Var{} = True
isSimpleExp Val{} = True
isSimpleExp (Ann _ x) = isSimpleExp x
isSimpleExp (Syn _ xs) = all isSimpleExp xs
isSimpleExp _ = False
{-|
Return the context that an expression bestows upon a hash or array
subscript. See 'reduce' for @\{\}@ and @\[\]@.
-}
inferExpCxt :: Exp -> Eval Cxt
inferExpCxt exp = return $ if isScalarLValue exp
then cxtItemAny
else cxtSlurpyAny
{-|
Evaluate the \'magical\' variable associated with a given name. Returns
@Nothing@ if the name does not match a known magical.
-}
getMagical :: Var -- ^ Name of the magical var to evaluate
-> Eval (Maybe Val)
getMagical var = Map.findWithDefault (return Nothing) var magicalMap
magicalMap :: Map Var (Eval (Maybe Val))
magicalMap = Map.fromList
[ (cast "$?FILE" , posSym ((cast . posName) :: Pos -> String))
, (cast "$?LINE" , posSym posBeginLine)
, (cast "$?COLUMN" , posSym posBeginColumn)
, (cast "$?POSITION" , posSym pretty)
, (cast "$?MODULE" , constSym "Main")
, (cast "$?OS" , constSym (getConfig "osname"))
, (cast "$?CLASS" , fmap (Just . VType . cast) (asks envPackage))
, (cast ":?CLASS" , fmap (Just . VType . cast) (asks envPackage))
, (cast "$?PACKAGE" , fmap (Just . VType . cast) (asks envPackage))
, (cast ":?PACKAGE" , fmap (Just . VType . cast) (asks envPackage))
, (cast "$?ROLE" , fmap (Just . VType . cast) (asks envPackage))
, (cast ":?ROLE" , fmap (Just . VType . cast) (asks envPackage))
]
posSym :: Value a => (Pos -> a) -> Eval (Maybe Val)
posSym f = fmap (Just . castV . f) $ asks envPos
constSym :: String -> Eval (Maybe Val)
constSym = return . Just . VStr
-- Find symbols, up and including multis.
findCodeSyms :: Var -> Eval [VCode]
findCodeSyms var
| isGlobalVar var = findWith findGlobal
| isQualifiedVar var = case dropVarPkg (__"OUTER") var of
Just var' -> die "outer - not yet implemented" var'
_ -> findWith findQualified
| otherwise = do
rv <- findWith findLexical
if null rv then findWith findPackage else return rv
where
findWith f = runMaybeT f >>= maybe (return []) return
-- $x should look up $x in the current pad first.
findLexical :: MaybeT Eval [VCode]
findLexical = do
lex <- lift $ asks envLexical
padSym lex var
-- $Foo::x is just $Foo::x, or maybe $*Foo::x.
findQualified :: MaybeT Eval [VCode]
findQualified = do
glob <- lift $ askGlobal
padSym glob var
`mplus` padSym glob (toGlobalVar var)
-- $x then fallbacks to $This::Package::x, or maybe $*x.
findPackage :: MaybeT Eval [VCode]
findPackage = do
-- XXX - This is bogus; pending Pad fixup code in Pugs.Parser
glob <- lift $ askGlobal
pkg <- lift $ asks envPackage
padSym glob (toPackage pkg var)
`mplus` padSym glob (toGlobalVar var)
-- $*Foo::x is just that.
findGlobal :: MaybeT Eval [VCode]
findGlobal = do
glob <- lift $ askGlobal
padSym glob (toGlobalVar var)
padSym :: Pad -> Var -> MaybeT Eval [VCode]
padSym pad var = do
case lookupPad var pad of
Just entry -> lift $ do
ref <- readPadEntry entry
readCodesFromRef ref
Nothing -> mzero
data ArityMatchData = MkArityMatchData
{ d_reqLen :: !Int
, d_optLen :: !Int
, d_slurpLen :: !Int
, d_hasArray :: !Bool
, d_hasHash :: !Bool
}
arityMatch :: VCode -> Int -> Int -> [Int] -> Maybe VCode
arityMatch sub@MkCode{ subAssoc = assoc, subParams = prms } posLen namLen argSlurpLens
| A_list <- assoc = Just sub
| A_chain <- assoc = Just sub
| argLen >= reqLen
, hasArray || ((if hasHash then posLen else argLen) <= (reqLen + optLen + slurpLen))
, if hasArray then slurpLen <= argSlurpLen else slurpLen == argSlurpLen
= Just sub
| otherwise
= Nothing
where
argLen = posLen + namLen
argSlurpLen = sum (drop (reqLen + optLen) argSlurpLens)
~(MkArityMatchData reqLen optLen slurpLen hasArray hasHash) = foldl unwindPrm initArityMatchData prms
initArityMatchData :: ArityMatchData
initArityMatchData = MkArityMatchData 0 0 0 False False
unwindPrm :: ArityMatchData -> Param -> ArityMatchData
unwindPrm dat p
| isSlurpy p = case v_sigil (paramName p) of
SArray -> dat{ d_hasArray = True }
SHash -> dat{ d_hasHash = True }
_ -> dat{ d_slurpLen = succ (d_slurpLen dat) }
| isOptional p = dat{ d_optLen = succ (d_optLen dat) }
| otherwise = dat{ d_reqLen = succ (d_reqLen dat) }
toPackage :: Pkg -> Var -> Var
toPackage pkg var
| isGlobalVar var = var
| otherwise = var{ v_package = pkg }
packageOf :: Var -> Pkg
packageOf = v_package
toQualified :: Var -> Eval Var
toQualified var@MkVar{ v_twigil = TNil, v_package = pkg }
| pkg == emptyPkg = do
currentPkg <- asks envPackage
return var{ v_package = currentPkg }
toQualified var = return var