atomo-0.4: src/Atomo/Parser/Expand.hs
module Atomo.Parser.Expand (doPragmas, macroExpand, nextPhase) where
import Control.Monad.State
import Atomo.Environment
import Atomo.Helpers
import Atomo.Method (addMethod, lookupMap)
import Atomo.Pattern (match)
import Atomo.Types
gensym :: Char
gensym = '!'
nextPhase :: [Expr] -> VM [Expr]
nextPhase es = do
mapM_ doPragmas es
mapM macroExpand es
doPragmas :: Expr -> VM ()
doPragmas (EDispatch { eMessage = em }) =
pragmas em
where
pragmas (Single { mTarget = t }) =
doPragmas t
pragmas (Keyword { mTargets = ts }) =
mapM_ doPragmas ts
doPragmas (EDefine { eExpr = e }) = do
doPragmas e
doPragmas (ESet { eExpr = e }) = do
doPragmas e
doPragmas (EBlock { eContents = es }) = do
mapM_ doPragmas es
doPragmas (EList { eContents = es }) = do
mapM_ doPragmas es
doPragmas (ETuple { eContents = es }) = do
mapM_ doPragmas es
doPragmas (EMacro { emPattern = p, eExpr = e }) =
addMacro p e
doPragmas (EParticle { eParticle = ep }) =
case ep of
Keyword { mTargets = mes } ->
forM_ mes $ \me ->
case me of
Nothing -> return ()
Just e -> doPragmas e
Single { mTarget = Just e } ->
doPragmas e
_ -> return ()
doPragmas (EMatch { eTarget = e, eBranches = bs }) = do
doPragmas e
forM_ bs (doPragmas . snd)
doPragmas (EOperator {}) = return ()
doPragmas (EPrimitive {}) = return ()
doPragmas (EForMacro { eExpr = e }) = do
env <- gets (psEnvironment . parserState)
macroExpand e >>= withTop env . eval
return ()
doPragmas (ETop {}) = return ()
doPragmas (EVM {}) = return ()
-- TODO: follow through EQuote into EUnquote
doPragmas (EQuote {}) = return ()
doPragmas (EUnquote {}) = return ()
doPragmas (ESetDynamic { eExpr = e }) =
doPragmas e
doPragmas (EDefineDynamic { eExpr = e }) =
doPragmas e
doPragmas (ENewDynamic { eBindings = bs, eExpr = e }) = do
mapM_ (\(_, b) -> doPragmas b) bs
doPragmas e
doPragmas (EGetDynamic {}) = return ()
doPragmas (EMacroQuote {}) = return ()
-- | Defines a macro, given its pattern and expression.
addMacro :: Message Pattern -> Expr -> VM ()
addMacro p e =
modify $ \env -> env
{ parserState = (parserState env)
{ psMacros = withMacro (psMacros (parserState env))
}
}
where
withMacro ms =
case p of
Single {} ->
(addMethod (Macro p e) (fst ms), snd ms)
Keyword {} ->
(fst ms, addMethod (Macro p e) (snd ms))
modifyPS :: (ParserState -> ParserState) -> VM ()
modifyPS f =
modify $ \e -> e
{ parserState = f (parserState e)
}
getPS :: VM ParserState
getPS = gets parserState
-- | Go through an expression recursively expanding macros. A dispatch
-- expression is checked to see if a macro was defined for it; if a macro is
-- found, its targets are sent to the macro method (unexpanded), and the
-- macro's result is expanded.
--
-- Every other expression just recursively calls macroExpand on any
-- sub-expressions.
macroExpand :: Expr -> VM Expr
macroExpand d@(EDispatch { eMessage = em }) = do
mm <- findMacro msg
case mm of
Just m -> do
modifyPS $ \ps -> ps { psClock = psClock ps + 1 }
eb <- gensyms (mExpr m) >>= macroExpand
Expression ne <-
runMethod (m { mExpr = eb }) msg
>>= findExpression
gensyms ne >>= macroExpand
Nothing -> do
nem <- expanded em
return d { eMessage = nem }
where
expanded s@(Single { mTarget = t }) = do
nt <- macroExpand t
return s { mTarget = nt }
expanded k@(Keyword { mTargets = ts }) = do
nts <- mapM macroExpand ts
return k { mTargets = nts }
msg =
case em of
Single i n t os -> Single i n (Expression t) (map exprOpt os)
Keyword i ns ts os -> Keyword i ns (map Expression ts) (map exprOpt os)
exprOpt (Option i n e) = Option i n (Expression e)
macroExpand e@(EMacroQuote { eName = n, eRaw = r, eFlags = fs }) = do
t <- gets (psEnvironment . parserState)
liftM (EPrimitive (eLocation e)) . dispatch $
keyword'
["quote", "as"]
[t, string r, particle n]
[option "flags" (list (map Character fs))]
macroExpand d@(EDefine { eExpr = e }) = do
e' <- macroExpand e
return d { eExpr = e' }
macroExpand s@(ESet { eExpr = e }) = do
e' <- macroExpand e
return s { eExpr = e' }
macroExpand b@(EBlock { eContents = es }) = do
nes <- mapM macroExpand es
return b { eContents = nes }
macroExpand l@(EList { eContents = es }) = do
nes <- mapM macroExpand es
return l { eContents = nes }
macroExpand t@(ETuple { eContents = es }) = do
nes <- mapM macroExpand es
return t { eContents = nes }
macroExpand p@(EParticle { eParticle = ep }) = do
nos <- forM (mOptionals ep) $ \(Option i n me) -> do
ne <- maybe (return Nothing) (liftM Just . macroExpand) me
return (Option i n ne)
case ep of
Keyword { mNames = ns, mTargets = mes } -> do
nmes <- forM mes $ \me ->
case me of
Nothing -> return Nothing
Just e -> liftM Just (macroExpand e)
return p { eParticle = keyword' ns nmes nos }
Single { mName = n, mTarget = Just e } -> do
ne <- macroExpand e
return p { eParticle = single' n (Just ne) nos }
_ -> return p
macroExpand s@(ESetDynamic { eExpr = e }) = do
e' <- macroExpand e
return s { eExpr = e' }
macroExpand d@(EDefineDynamic { eExpr = e }) = do
e' <- macroExpand e
return d { eExpr = e' }
macroExpand n@(ENewDynamic { eBindings = bs, eExpr = e }) = do
bs' <- mapM (\(p, b) -> macroExpand b >>= \nb -> return (p, nb)) bs
e' <- macroExpand e
return n { eBindings = bs', eExpr = e' }
macroExpand m@(EMatch { eTarget = t, eBranches = bs }) = do
nt <- macroExpand t
nbs <- forM bs $ \(p, e) -> do
ne <- macroExpand e
return (p, ne)
return m { eTarget = nt, eBranches = nbs }
macroExpand m@(EMacro {}) = return m
macroExpand e@(EGetDynamic {}) = return e
macroExpand e@(EOperator {}) = return e
macroExpand e@(EPrimitive {}) = return e
macroExpand e@(EForMacro {}) = return e
macroExpand e@(ETop {}) = return e
macroExpand e@(EVM {}) = return e
macroExpand e@(EQuote {}) = expandQuote e
macroExpand e@(EUnquote {}) = return e
expandQuote :: Expr -> VM Expr
expandQuote = throughQuotes 0 $ \n e ->
case n of
0 -> macroExpand e
_ -> return e
gensyms :: Expr -> VM Expr
gensyms = throughQuotes 0 $ \_ e ->
case e of
EDispatch { eMessage = m@(Single { mName = x:xs }) }
| x == gensym -> do
c <- gets (psClock . parserState)
return e
{ eMessage = single'
(xs ++ ":" ++ show c)
(mTarget m)
(mOptionals m)
}
_ -> return e
throughQuotes :: Int -> (Int -> Expr -> VM Expr) -> Expr -> VM Expr
throughQuotes 0 f u@(EUnquote { eExpr = e }) = do
ne <- f 0 e
return u { eExpr = ne }
throughQuotes n f u@(EUnquote { eExpr = a }) = do
ne <- throughQuotes (n - 1) f a
f n u { eExpr = ne }
-- don't expand through definitions, as gensyms in those
-- are likely used elsewhere where they'll be expanded
throughQuotes n f d@(EDefine {}) = f n d
throughQuotes n f s@(ESet { ePattern = p, eExpr = e }) = do
np <- expandPattern p
ne <- throughQuotes n f e
f n s { ePattern = np, eExpr = ne }
throughQuotes n f d@(EDispatch { eMessage = m@(Keyword {}) }) = do
nts <- mapM (throughQuotes n f) (mTargets m)
f n d { eMessage = m { mTargets = nts } }
throughQuotes n f d@(EDispatch { eMessage = m@(Single {}) }) = do
nt <- throughQuotes n f (mTarget m)
f n d { eMessage = m { mTarget = nt } }
throughQuotes n f b@(EBlock { eArguments = ps, eContents = es }) = do
nps <- mapM expandPattern ps
nes <- mapM (throughQuotes n f) es
f n b { eArguments = nps, eContents = nes }
throughQuotes n f l@(EList { eContents = es }) = do
nes <- mapM (throughQuotes n f) es
f n l { eContents = nes }
throughQuotes n f t@(ETuple { eContents = es }) = do
nes <- mapM (throughQuotes n f) es
f n t { eContents = nes }
throughQuotes n f m@(EMacro { eExpr = e }) = do
ne <- throughQuotes n f e
f n m { eExpr = ne }
throughQuotes n f p@(EParticle { eParticle = m@(Keyword { mTargets = mes }) }) = do
nmes <- forM mes $ maybe (return Nothing) (liftM Just . throughQuotes n f)
f n p { eParticle = m { mTargets = nmes } }
throughQuotes n f p@(EParticle { eParticle = m@(Single { mTarget = me }) }) = do
nme <- maybe (return Nothing) (liftM Just . throughQuotes n f) me
f n p { eParticle = m { mTarget = nme } }
throughQuotes n f s@(ESetDynamic { eExpr = e }) = do
e' <- throughQuotes n f e
f n s { eExpr = e' }
throughQuotes n f d@(EDefineDynamic { eExpr = e }) = do
e' <- throughQuotes n f e
f n d { eExpr = e' }
throughQuotes n f d@(ENewDynamic { eBindings = bs, eExpr = e }) = do
nbs <- mapM (\(p, b) -> f n b >>= \nb -> return (p, nb)) bs
ne <- throughQuotes n f e
f n d { eBindings = nbs, eExpr = ne }
throughQuotes n f q@(EQuote { eExpr = e }) = do
ne <- throughQuotes (n + 1) f e
f (n + 1) q { eExpr = ne }
throughQuotes n f m@(EMatch { eTarget = t, eBranches = bs }) = do
nt <- throughQuotes n f t
nbs <- mapM (\(p, b) -> f n b >>= \nb -> return (p, nb)) bs
f n m { eTarget = nt, eBranches = nbs }
throughQuotes n f e = f n e
expandPattern :: Pattern -> VM Pattern
expandPattern (PNamed n p)
| head n == gensym = do
c <- gets (psClock . parserState)
np <- expandPattern p
return (PNamed (tail n ++ ":" ++ show c) np)
| otherwise = liftM (PNamed n) (expandPattern p)
expandPattern (PHeadTail h t) =
liftM2 PHeadTail (expandPattern h) (expandPattern t)
expandPattern (PList ps) =
liftM PList (mapM expandPattern ps)
expandPattern (PTuple ps) =
liftM PTuple (mapM expandPattern ps)
expandPattern (PMessage (m@(Single { mTarget = t }))) = do
nt <- expandPattern t
return $ PMessage m { mTarget = nt }
expandPattern (PMessage (m@(Keyword { mTargets = ts }))) = do
nts <- mapM expandPattern ts
return $ PMessage m { mTargets = nts }
expandPattern (PInstance p) =
liftM PInstance (expandPattern p)
expandPattern (PStrict p) =
liftM PStrict (expandPattern p)
expandPattern (PVariable p) =
liftM PVariable (expandPattern p)
expandPattern (PExpr p) =
liftM PExpr (expandQuote p)
expandPattern (PPMKeyword ns ps) =
liftM (PPMKeyword ns) (mapM expandPattern ps)
expandPattern p = return p
-- | find a findMacro method for message `m' on object `o'
findMacro :: Message Value -> VM (Maybe Method)
findMacro m = do
ids <- gets primitives
ms <- methods m
return $ maybe Nothing (firstMatch ids m) (lookupMap (mID m) ms)
where
methods (Single {}) = liftM (fst . psMacros) getPS
methods (Keyword {}) = liftM (snd . psMacros) getPS
firstMatch _ _ [] = Nothing
firstMatch ids' m' (mt:mts)
| match ids' Nothing (PMessage (mPattern mt)) (Message m') =
Just mt
| otherwise = firstMatch ids' m' mts