atomo-0.2.2.1: src/Atomo/Parser/Expand.hs
module Atomo.Parser.Expand (macroExpand) where
import "monads-fd" Control.Monad.State
import Text.Parsec
import qualified "mtl" Control.Monad.Trans as MTL
import Atomo.Environment
import Atomo.Helpers
import Atomo.Method (lookupMap)
import Atomo.Parser.Base
import Atomo.Pattern (match)
import Atomo.Types
-- | 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 -> Parser Expr
macroExpand d@(Dispatch { eMessage = em }) = do
mm <- findMacro msg
case mm of
Just m -> do
modifyState $ \ps -> ps { psClock = psClock ps + 1 }
Expression e <-
MTL.lift (runMethod m msg >>= findExpression)
macroExpand e
Nothing -> do
nem <- expanded em
return d { eMessage = nem }
where
expanded (ESingle i n t) = do
nt <- macroExpand t
return (ESingle i n nt)
expanded (EKeyword i ns ts) = do
nts <- mapM macroExpand ts
return (EKeyword i ns nts)
msg =
case em of
ESingle i n t -> Single i n (Expression t)
EKeyword i ns ts -> Keyword i ns (map Expression ts)
macroExpand d@(Define { eExpr = e }) = do
e' <- macroExpand e
return d { eExpr = e' }
macroExpand s@(Set { 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 m@(EMacro { eExpr = e }) = do -- TODO: is this sane?
e' <- macroExpand e
return m { eExpr = e' }
macroExpand p@(EParticle { eParticle = ep }) =
case ep of
EPMKeyword ns mes -> do
nmes <- forM mes $ \me ->
case me of
Nothing -> return Nothing
Just e -> liftM Just (macroExpand e)
return p { eParticle = EPMKeyword ns nmes }
_ -> return p
-- TODO: EUnquote?
macroExpand e = return e
-- | find a findMacro method for message `m' on object `o'
findMacro :: Message -> Parser (Maybe Method)
findMacro m = do
ids <- MTL.lift (gets primitives)
ms <- methods m
maybe (return Nothing) (firstMatch ids m) (lookupMap (mID m) ms)
where
methods (Single {}) = liftM (fst . psMacros) getState
methods (Keyword {}) = liftM (snd . psMacros) getState
firstMatch _ _ [] = return Nothing
firstMatch ids' m' (mt:mts)
| match ids' Nothing (mPattern mt) (Message m') = return (Just mt)
| otherwise = firstMatch ids' m' mts