module Scheme.Macro
(
macroEval
) where
import Scheme.Types
import Scheme.Variables
import Control.Monad
import Control.Monad.Error
import Debug.Trace
macroEval :: Env -> LispVal -> IOThrowsError LispVal
macroEval env (List [Atom "define-syntax", Atom keyword, syntaxRules@(List (Atom "syntax-rules" : (List identifiers : rules)))]) = do
defineNamespacedVar env macroNamespace keyword syntaxRules
return $ Nil ""
macroEval env lisp@(List (x@(List _) : xs)) = do
first <- macroEval env x
rest <- mapM (macroEval env) xs
return $ List $ first : rest
macroEval env lisp@(List (Atom x : xs)) = do
isDefined <- liftIO $ isNamespacedBound env macroNamespace x
if isDefined
then do
syntaxRules@(List (Atom "syntax-rules" : (List identifiers : rules))) <- getNamespacedVar env macroNamespace x
macroEval env =<< macroTransform env (List identifiers) rules lisp
else do
rest <- mapM (macroEval env) xs
return $ List $ (Atom x) : rest
macroEval _ lisp@(_) = return lisp
macroTransform :: Env -> LispVal -> [LispVal] -> LispVal -> IOThrowsError LispVal
macroTransform env identifiers rules@(rule@(List r) : rs) input = do
localEnv <- liftIO $ nullEnv
result <- matchRule env identifiers localEnv rule input
case result of
Nil _ -> macroTransform env identifiers rs input
otherwise -> return result
macroTransform _ _ _ input = throwError $ BadSpecialForm "Input does not match a macro pattern" input
macroElementMatchesMany :: LispVal -> Bool
macroElementMatchesMany (List (p:ps)) = do
if not (null ps)
then case (head ps) of
Atom "..." -> True
otherwise -> False
else False
macroElementMatchesMany _ = False
matchRule :: Env -> LispVal -> Env -> LispVal -> LispVal -> IOThrowsError LispVal
matchRule env identifiers localEnv (List [pattern, template]) (List inputVar) = do
let is = tail inputVar
let p = case pattern of
DottedList ds d -> case ds of
(Atom l : ls) -> List [Atom l, DottedList ls d]
otherwise -> pattern
otherwise -> pattern
case p of
List (Atom _ : ps) -> do
match <- loadLocal localEnv identifiers (List ps) (List is) False False
case match of
Bool False -> return $ Nil ""
otherwise -> transformRule localEnv 0 (List []) template (List [])
otherwise -> throwError $ BadSpecialForm "Malformed rule in syntax-rules" p
matchRule _ identifiers _ rule input = do
throwError $ BadSpecialForm "Malformed rule in syntax-rules" $ List [Atom "rule: ", rule, Atom "input: ", input]
loadLocal :: Env -> LispVal -> LispVal -> LispVal -> Bool -> Bool -> IOThrowsError LispVal
loadLocal localEnv identifiers pattern input hasEllipsis outerHasEllipsis = do
case (pattern, input) of
((DottedList ps p), (DottedList is i)) -> do
result <- loadLocal localEnv identifiers (List ps) (List is) False outerHasEllipsis
case result of
Bool True -> loadLocal localEnv identifiers p i False outerHasEllipsis
otherwise -> return $ Bool False
(List (p:ps), List (i:is)) -> do
let hasEllipsis = macroElementMatchesMany pattern
status <- checkLocal localEnv identifiers (hasEllipsis || outerHasEllipsis) p i
case status of
Bool False -> if hasEllipsis
then do
loadLocal localEnv identifiers (List $ tail ps) (List (i:is)) False outerHasEllipsis
else return $ Bool False
otherwise -> if hasEllipsis
then loadLocal localEnv identifiers pattern (List is) True outerHasEllipsis
else loadLocal localEnv identifiers (List ps) (List is) False outerHasEllipsis
(List [], List []) -> return $ Bool True
(List (p:ps), List []) -> do
initializePatternVars localEnv "list" identifiers pattern
let hasEllipsis = macroElementMatchesMany pattern
if hasEllipsis && ((length ps) == 1)
then return $ Bool True
else return $ Bool False
(List [], _) -> return $ Bool False
(_, _) -> checkLocal localEnv identifiers (hasEllipsis || outerHasEllipsis) pattern input
checkLocal :: Env -> LispVal -> Bool -> LispVal -> LispVal -> IOThrowsError LispVal
checkLocal localEnv identifiers hasEllipsis (Bool pattern) (Bool input) = return $ Bool $ pattern == input
checkLocal localEnv identifiers hasEllipsis (Number pattern) (Number input) = return $ Bool $ pattern == input
checkLocal localEnv identifiers hasEllipsis (Float pattern) (Float input) = return $ Bool $ pattern == input
checkLocal localEnv identifiers hasEllipsis (String pattern) (String input) = return $ Bool $ pattern == input
checkLocal localEnv identifiers hasEllipsis (Char pattern) (Char input) = return $ Bool $ pattern == input
checkLocal localEnv identifiers hasEllipsis (Atom pattern) input = do
if hasEllipsis
then do isDefined <- liftIO $ isBound localEnv pattern
found <- findAtom (Atom pattern) identifiers
let val = case found of
(Bool True) -> Atom pattern
otherwise -> input
if isDefined
then do v <- getVar localEnv pattern
case v of
(List vs) -> setVar localEnv pattern (List $ vs ++ [val])
else defineVar localEnv pattern (List [val])
else defineVar localEnv pattern input
return $ Bool True
checkLocal localEnv identifiers hasEllipsis pattern@(DottedList ps p) input@(DottedList is i) =
loadLocal localEnv identifiers pattern input False hasEllipsis
checkLocal localEnv identifiers hasEllipsis pattern@(DottedList ps p) input@(List (i : is)) = do
if (length ps) == (length is)
then loadLocal localEnv identifiers (List $ ps ++ [p]) input False hasEllipsis
else loadLocal localEnv identifiers pattern (DottedList (i : is) (List [])) False hasEllipsis
checkLocal localEnv identifiers hasEllipsis pattern@(List _) input@(List _) =
loadLocal localEnv identifiers pattern input False hasEllipsis
checkLocal localEnv identifiers hasEllipsis _ _ = return $ Bool False
transformRule :: Env -> Int -> LispVal -> LispVal -> LispVal -> IOThrowsError LispVal
transformRule localEnv ellipsisIndex (List result) transform@(List(List l : ts)) (List ellipsisList) = do
if macroElementMatchesMany transform
then do
curT <- transformRule localEnv (ellipsisIndex + 1) (List []) (List l) (List result)
case curT of
Nil _ -> if ellipsisIndex == 0
then transformRule localEnv 0 (List $ result) (List $ tail ts) (List [])
else transformRule localEnv 0 (List $ ellipsisList ++ result) (List $ tail ts) (List [])
List [Nil _, List elst] -> if ellipsisIndex == 0
then transformRule localEnv 0 (List $ result) (List $ tail ts) (List [])
else transformRule localEnv 0 (List $ result) (List $ tail ts) (List [])
List t -> transformRule localEnv (ellipsisIndex + 1) (List $ result ++ [curT]) transform (List ellipsisList)
else do
lst <- transformRule localEnv ellipsisIndex (List []) (List l) (List ellipsisList)
case lst of
List [Nil _, l] -> return lst
List _ -> transformRule localEnv ellipsisIndex (List $ result ++ [lst]) (List ts) (List ellipsisList)
Nil _ -> return lst
otherwise -> throwError $ BadSpecialForm "Macro transform error" $ List [lst, (List l), Number $ toInteger ellipsisIndex]
where lastElementIsNil l = case (last l) of
Nil _ -> True
otherwise -> False
getListAtTail l = case (last l) of
List lst -> lst
transformRule localEnv ellipsisIndex (List result) transform@(List (dl@(DottedList ds d) : ts)) (List ellipsisList) = do
if macroElementMatchesMany transform
then do
curT <- transformDottedList localEnv (ellipsisIndex + 1) (List []) (List [dl]) (List result)
case curT of
Nil _ -> if ellipsisIndex == 0
then transformRule localEnv 0 (List $ result) (List $ tail ts) (List [])
else transformRule localEnv 0 (List $ ellipsisList ++ result) (List $ tail ts) (List [])
List [Nil _, List elst] -> if ellipsisIndex == 0
then transformRule localEnv 0 (List $ result) (List $ tail ts) (List [])
else transformRule localEnv 0 (List $ result) (List $ tail ts) (List [])
List t -> transformRule localEnv (ellipsisIndex + 1) (List $ result ++ t) transform (List ellipsisList)
else do lst <- transformDottedList localEnv ellipsisIndex (List []) (List [dl]) (List ellipsisList)
case lst of
List [Nil _, List l] -> return lst
List l -> transformRule localEnv ellipsisIndex (List $ result ++ l) (List ts) (List ellipsisList)
Nil n -> return lst
otherwise -> throwError $ BadSpecialForm "transformRule: Macro transform error" $ List [(List ellipsisList), lst, (List [dl]), Number $ toInteger ellipsisIndex]
where transformDottedList :: Env -> Int -> LispVal -> LispVal -> LispVal -> IOThrowsError LispVal
transformDottedList localEnv ellipsisIndex (List result) transform@(List (DottedList ds d : ts)) (List ellipsisList) = do
lsto <- transformRule localEnv ellipsisIndex (List []) (List ds) (List ellipsisList)
case lsto of
List lst -> do
r <- transformRule localEnv ellipsisIndex (List []) (List [d]) (List ellipsisList)
case r of
List [List []] -> transformRule localEnv ellipsisIndex (List $ result ++ [List lst]) (List ts) (List ellipsisList)
List [rst] -> do
src <- lookupPatternVarSrc localEnv $ List ds
case src of
String "pair" -> transformRule localEnv ellipsisIndex (List $ result ++ [DottedList lst rst]) (List ts) (List ellipsisList)
otherwise -> transformRule localEnv ellipsisIndex (List $ result ++ [List $ lst ++ [rst]]) (List ts) (List ellipsisList)
otherwise -> throwError $ BadSpecialForm "Macro transform error processing pair" $ DottedList ds d
Nil _ -> return $ List [Nil "", List ellipsisList]
otherwise -> throwError $ BadSpecialForm "Macro transform error processing pair" $ DottedList ds d
transformRule localEnv ellipsisIndex (List result) transform@(List (Atom a : ts)) unused = do
let hasEllipsis = macroElementMatchesMany transform
isDefined <- liftIO $ isBound localEnv a
if hasEllipsis
then if isDefined
then do
var <- getVar localEnv a
case var of
List v -> transformRule localEnv ellipsisIndex (List $ result ++ v) (List $ tail ts) unused
v@(_) -> transformRule localEnv ellipsisIndex (List $ result ++ [v]) (List $ tail ts) unused
else
transformRule localEnv ellipsisIndex (List result) (List $ tail ts) unused
else do t <- if isDefined
then do var <- getVar localEnv a
if ellipsisIndex > 0
then do case var of
List v -> if (length v) > (ellipsisIndex 1)
then return $ v !! (ellipsisIndex 1)
else return $ Nil ""
else return var
else return $ Atom a
case t of
Nil _ -> return t
otherwise -> transformRule localEnv ellipsisIndex (List $ result ++ [t]) (List ts) unused
transformRule localEnv ellipsisIndex (List result) transform@(List (t : ts)) (List ellipsisList) = do
transformRule localEnv ellipsisIndex (List $ result ++ [t]) (List ts) (List ellipsisList)
transformRule localEnv ellipsisIndex result@(List _) transform@(List []) unused = do
return result
transformRule localEnv ellipsisIndex result transform unused = do
throwError $ BadSpecialForm "An error occurred during macro transform" $ List [(Number $ toInteger ellipsisIndex), result, transform, unused]
findAtom :: LispVal -> LispVal -> IOThrowsError LispVal
findAtom (Atom target) (List (Atom a:as)) = do
if target == a
then return $ Bool True
else findAtom (Atom target) (List as)
findAtom target (List (badtype : _)) = throwError $ TypeMismatch "symbol" badtype
findAtom target _ = return $ Bool False
initializePatternVars :: Env -> String -> LispVal -> LispVal -> IOThrowsError LispVal
initializePatternVars localEnv src identifiers pattern@(List _) = do
case pattern of
List (p:ps) -> do initializePatternVars localEnv src identifiers p
initializePatternVars localEnv src identifiers $ List ps
List [] -> return $ Bool True
initializePatternVars localEnv src identifiers pattern@(DottedList ps p) = do
initializePatternVars localEnv src identifiers $ List ps
initializePatternVars localEnv src identifiers p
initializePatternVars localEnv src identifiers (Atom pattern) =
do defineNamespacedVar localEnv "src" pattern $ String src
isDefined <- liftIO $ isBound localEnv pattern
found <- findAtom (Atom pattern) identifiers
case found of
(Bool False) -> if not isDefined
then do
defineVar localEnv pattern (List [])
else do
return $ Bool True
otherwise -> return $ Bool True
initializePatternVars localEnv src identifiers pattern =
return $ Bool True
lookupPatternVarSrc :: Env -> LispVal -> IOThrowsError LispVal
lookupPatternVarSrc localEnv pattern@(List _) = do
case pattern of
List (p:ps) -> do result <- lookupPatternVarSrc localEnv p
case result of
Bool False -> lookupPatternVarSrc localEnv $ List ps
otherwise -> return result
List [] -> return $ Bool False
lookupPatternVarSrc localEnv pattern@(DottedList ps p) = do
result <- lookupPatternVarSrc localEnv $ List ps
case result of
Bool False -> lookupPatternVarSrc localEnv p
otherwise -> return result
lookupPatternVarSrc localEnv (Atom pattern) =
do isDefined <- liftIO $ isNamespacedBound localEnv "src" pattern
if isDefined then getNamespacedVar localEnv "src" pattern
else return $ Bool False
lookupPatternVarSrc localEnv pattern =
return $ Bool False