packages feed

husk-scheme 3.6.1 → 3.6.2

raw patch · 18 files changed

+2616/−2446 lines, 18 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Language.Scheme.Compiler: astToHaskellStr :: LispVal -> String
+ Language.Scheme.Compiler: ast2Str :: LispVal -> String
+ Language.Scheme.Compiler: asts2Str :: [LispVal] -> String
+ Language.Scheme.Compiler: compileDivertedVars :: String -> Env -> [LispVal] -> CompOpts -> IOThrowsError HaskAST
+ Language.Scheme.Compiler: divertVars :: Env -> LispVal -> CompOpts -> (Env -> LispVal -> CompOpts -> IOThrowsError [HaskAST]) -> IOThrowsError [HaskAST]
+ Language.Scheme.Core: registerExtensions :: Env -> (FilePath -> IO FilePath) -> IO ()
+ Language.Scheme.Macro: getDivertedVars :: Env -> IOThrowsError [LispVal]
+ Language.Scheme.Types: LispEnv :: Env -> LispVal
- Language.Scheme.Compiler: header :: [String]
+ Language.Scheme.Compiler: header :: String -> [String]

Files

AUTHORS view
@@ -7,3 +7,9 @@   Arseniy <https://github.com/Rotsor>   Eli Barzilay <eli@barzilay.org>   Ricardo Lanziano <ricardo.lanziano@gmail.com>++References:+  Write Yourself a Scheme in 48 Hours <http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours>+  R5RS <http://www.schemers.org/Documents/Standards/R5RS/HTML/>+  CHICKEN <http://www.call-cc.org/>+  Chibi-Scheme <http://code.google.com/p/chibi-scheme/>
ChangeLog.markdown view
@@ -1,3 +1,28 @@+v3.6.2+--------+This release adds support for nested quasi-quotation forms, which now respect depth level. This was done by replacing the quasi-quotation special form with a macro based on the one from chibi scheme. A nice side-benefit is that by removing the special forms, quasi-quotation now works in the compiler.++Also added support for SRFI 2, `and-let*`. From the SRFI document:++> Like an ordinary AND, an AND-LET\* special form evaluates its arguments - expressions - one after another in order, until the first one that yields #f. Unlike AND, however, a non-#f result of one expression can be bound to a fresh variable and used in the subsequent expressions. AND-LET\* is a cross-breed between LET\* and AND.++And added support for environment specifiers, including the following functions:++- `(interaction-environment)`+- `(current-environment)`+- `(make-environment)`+- `(null-environment version)`+- `(load filename environment-specifier)`+- `(eval expression environment-specifier)`++This release also includes the following bug fixes:++- Fixed a bug where nested explicit renaming macros may not always expand properly.+- Unfortunately, the storage model changes introduced in 3.6 cause problems with hash table literals defined using the `#hash()` syntax. For now, hash table literals have been removed to prevent further problems. This feature may be added back in the future.+- Fixed the code for `require-extension` to allow passing multiple SRFI numbers in the same call. For example: `(require-extension (srfi 1 2))`.+- Improved compiler support by diverting renamed variables back into the enclosing environment. Such variables would previously throw a runtime error when accessed by the compiled program.+- Improved compiler support by loading macros defined using `define-syntax` so they are available at runtime.+ v3.6.1 -------- Added support for GHC 7.6.
hs-src/Compiler/huskc.hs view
@@ -115,7 +115,7 @@ -- |Print debug information showDebug :: Options -> IO Options showDebug _ = do-  stdlib <- getDataFileName "stdlib.scm"+  stdlib <- getDataFileName "lib/stdlib.scm"   putStrLn $ "stdlib: " ++ stdlib   putStrLn ""   exitWith ExitSuccess@@ -131,22 +131,13 @@ process :: String -> String -> Bool -> String -> IO () process inFile outExec dynamic extraArgs = do   env <- Language.Scheme.Core.primitiveBindings-  stdlib <- getDataFileName "stdlib.scm"-  srfi55 <- getDataFileName "srfi/srfi-55.scm" -- (require-extension)+  stdlib <- getDataFileName "lib/stdlib.scm"+  srfi55 <- getDataFileName "lib/srfi/srfi-55.scm" -- (require-extension)   result <- (runIOThrows $ liftM show $ compileSchemeFile env stdlib srfi55 inFile)   case result of    Just errMsg -> putStrLn errMsg    _ -> compileHaskellFile outExec dynamic extraArgs --- TODO: this is also used in shell.hs - find a common place to put them both--- |Register the given SRFI-registerSRFI :: Env -> Integer -> IO ()-registerSRFI env num = do- filename <- getDataFileName $ "srfi/srfi-" ++ show num ++ ".scm"- _ <- Language.Scheme.Core.evalString env $ "(register-extension '(srfi " ++ show num ++ ") \"" ++ -  (Language.Scheme.Core.escapeBackslashes filename) ++ "\")"- return () - -- |Compile a scheme file to haskell compileSchemeFile :: Env -> String -> String -> String -> IOThrowsError LispVal compileSchemeFile env stdlib srfi55 filename = do@@ -155,8 +146,8 @@   -- TODO: it is only temporary to compile the standard library each time. It should be    --       precompiled and just added during the ghc compilation   libsC <- compileLisp env stdlib "run" (Just "exec55")-  libSrfi55C <- compileLisp env srfi55 "exec55" (Just "exec")-  _ <- liftIO $ registerSRFI env 1+  libSrfi55C <- compileLisp env srfi55 "exec55" (Just "exec55_2")+  liftIO $ Language.Scheme.Core.registerExtensions env getDataFileName    -- Initialize the compiler module and begin   _ <- initializeCompiler env@@ -169,7 +160,8 @@   outH <- liftIO $ openFile "_tmp.hs" WriteMode   _ <- liftIO $ writeList outH headerModule   _ <- liftIO $ writeList outH $ map (\mod -> "import " ++ mod ++ " ") $ headerImports ++ moreHeaderImports-  _ <- liftIO $ writeList outH header+  filepath <- liftIO $ getDataFileName ""+  _ <- liftIO $ writeList outH $ header filepath   _ <- liftIO $ writeList outH $ map show libsC   _ <- liftIO $ writeList outH $ map show libSrfi55C   _ <- liftIO $ writeList outH $ map show execC
hs-src/Interpreter/shell.hs view
@@ -57,21 +57,13 @@ -- |Load standard libraries into the given environment loadLibraries :: Env -> IO () loadLibraries env = do-  stdlib <- getDataFileName "stdlib.scm"-  srfi55 <- getDataFileName "srfi/srfi-55.scm" -- (require-extension)+  stdlib <- getDataFileName "lib/stdlib.scm"+  srfi55 <- getDataFileName "lib/srfi/srfi-55.scm" -- (require-extension)   -- Load standard library   _ <- evalString env $ "(load \"" ++ (escapeBackslashes stdlib) ++ "\")"    -- Load (require-extension), which can be used to load other SRFI's   _ <- evalString env $ "(load \"" ++ (escapeBackslashes srfi55) ++ "\")"-  registerSRFI env 1---- |Register the given SRFI-registerSRFI :: Env -> Integer -> IO ()-registerSRFI env num = do- filename <- getDataFileName $ "srfi/srfi-" ++ show num ++ ".scm"- _ <- evalString env $ "(register-extension '(srfi " ++ show num ++ ") \"" ++ -  (escapeBackslashes filename) ++ "\")"- return ()+  registerExtensions env getDataFileName  -- |Start the REPL (interactive interpreter) runRepl :: IO ()
hs-src/Language/Scheme/Compiler.hs view
@@ -22,10 +22,8 @@ -}  module Language.Scheme.Compiler where -import qualified Language.Scheme.Core (apply)+import qualified Language.Scheme.Core (apply, escapeBackslashes) import qualified Language.Scheme.Macro--- import Language.Scheme.Numerical--- import Language.Scheme.Parser import Language.Scheme.Primitives import Language.Scheme.Types import Language.Scheme.Variables@@ -35,7 +33,6 @@ import qualified Data.List import qualified Data.Map import Data.Ratio--- import System.IO --import Debug.Trace  -- A type to store options passed to compile@@ -94,29 +91,35 @@ joinL :: forall a. [[a]] -> [a] -> [a] joinL ls sep = concat $ Data.List.intersperse sep ls -astToHaskellStr :: LispVal -> String -astToHaskellStr (String s) = "String " ++ show s-astToHaskellStr (Char c) = "Char " ++ show c-astToHaskellStr (Atom a) = "Atom " ++ show a-astToHaskellStr (Number n) = "Number (" ++ show n ++ ")"-astToHaskellStr (Complex c) = "Complex $ (" ++ (show $ realPart c) ++ ") :+ (" ++ (show $ imagPart c) ++ ")"-astToHaskellStr (Rational r) = "Rational $ (" ++ (show $ numerator r) ++ ") % (" ++ (show $ denominator r) ++ ")"-astToHaskellStr (Float f) = "Float (" ++ show f ++ ")"-astToHaskellStr (Bool True) = "Bool True"-astToHaskellStr (Bool False) = "Bool False"-astToHaskellStr (HashTable ht) = do+-- |Convert abstract syntax tree to a string+ast2Str :: LispVal -> String +ast2Str (String s) = "String " ++ show s+ast2Str (Char c) = "Char " ++ show c+ast2Str (Atom a) = "Atom " ++ show a+ast2Str (Number n) = "Number (" ++ show n ++ ")"+ast2Str (Complex c) = "Complex $ (" ++ (show $ realPart c) ++ ") :+ (" ++ (show $ imagPart c) ++ ")"+ast2Str (Rational r) = "Rational $ (" ++ (show $ numerator r) ++ ") % (" ++ (show $ denominator r) ++ ")"+ast2Str (Float f) = "Float (" ++ show f ++ ")"+ast2Str (Bool True) = "Bool True"+ast2Str (Bool False) = "Bool False"+ast2Str (HashTable ht) = do  let ls = Data.Map.toList ht -     conv (a, b) = "(" ++ astToHaskellStr a ++ "," ++ astToHaskellStr b ++ ")"+     conv (a, b) = "(" ++ ast2Str a ++ "," ++ ast2Str b ++ ")"  "HashTable $ Data.Map.fromList $ [" ++ joinL (map conv ls) "," ++ "]"-astToHaskellStr (Vector v) = do+ast2Str (Vector v) = do   let ls = Data.Array.elems v       size = (length ls) - 1-  "Vector (listArray (0, " ++ show size ++ ")" ++ "[" ++ joinL (map astToHaskellStr ls) "," ++ "])"-astToHaskellStr (List ls) = "List [" ++ joinL (map astToHaskellStr ls) "," ++ "]"-astToHaskellStr (DottedList ls l) = -  "DottedList [" ++ joinL (map astToHaskellStr ls) "," ++ "] $ " ++ astToHaskellStr l+  "Vector (listArray (0, " ++ show size ++ ")" ++ "[" ++ joinL (map ast2Str ls) "," ++ "])"+ast2Str (List ls) = "List [" ++ joinL (map ast2Str ls) "," ++ "]"+ast2Str (DottedList ls l) = +  "DottedList [" ++ joinL (map ast2Str ls) "," ++ "] $ " ++ ast2Str l -header, headerModule, headerImports :: [String]+-- |Convert a list of abstract syntax trees to a list of strings+asts2Str :: [LispVal] -> String+asts2Str ls = do+    "[" ++ (joinL (map ast2Str ls) ",") ++ "]"++headerModule, headerImports :: [String] headerModule = ["module Main where "] headerImports = [    "Language.Scheme.Core "@@ -130,17 +133,26 @@  , " qualified Data.Map "  , "Data.Ratio "  , "System.IO "]-header = [-   " "- , "main :: IO () "- , "main = do "- , "  env <- primitiveBindings "- , "  result <- (runIOThrows $ liftM show $ run env (makeNullContinuation env) (Nil \"\") Nothing) "- , "  case result of "- , "    Just errMsg -> putStrLn errMsg "- , "    _ -> return () "- , " "] +header :: String -> [String]+header filepath = do+  [ " "+    , "getDataFileName' :: FilePath -> IO FilePath "+    , "getDataFileName' name = return $ \"" ++ (Language.Scheme.Core.escapeBackslashes filepath) ++ "\" ++ name "+    , " "+    , "exec55_2 env cont _ _ = do "+    , "  liftIO $ registerExtensions env getDataFileName' "+    , "  continueEval env (makeCPS env cont exec) (Nil \"\")"+    , " "+    , "main :: IO () "+    , "main = do "+    , "  env <- primitiveBindings "+    , "  result <- (runIOThrows $ liftM show $ run env (makeNullContinuation env) (Nil \"\") Nothing) "+    , "  case result of "+    , "    Just errMsg -> putStrLn errMsg "+    , "    _ -> return () "+    , " "]+ -- NOTE: the following type is used for all functions generated by the compiler:  -- , "run :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal " @@ -196,8 +208,8 @@ compile _ (Number n) copts = compileScalar ("  return $ Number (" ++ (show n) ++ ")") copts compile _ (Bool b) copts = compileScalar ("  return $ Bool " ++ (show b)) copts -- TODO: eval env cont val@(HashTable _) = continueEval env cont val-compile _ v@(Vector _) copts = compileScalar (" return $ " ++ astToHaskellStr v) copts-compile _ ht@(HashTable _) copts = compileScalar (" return $ " ++ astToHaskellStr ht) copts+compile _ v@(Vector _) copts = compileScalar (" return $ " ++ ast2Str v) copts+compile _ ht@(HashTable _) copts = compileScalar (" return $ " ++ ast2Str ht) copts compile _ (Atom a) copts = do  c <- return $ createAstCont copts "val" ""  return [createAstFunc copts [@@ -210,19 +222,12 @@    AstValue $ "    HashTable _ -> Pointer \"" ++ a ++ "\" env",    AstValue $ "    _ -> v"], c] -compile _ (List [Atom "quote", val]) copts = compileScalar (" return $ " ++ astToHaskellStr val) copts----- TODO: eval envi cont (List [Atom "quasiquote", value]) = cpsUnquote envi cont value Nothing--- --- This is only a temporary solution that does not handle unquoting----compile _ (List [Atom "quasiquote", val]) copts = compileScalar (" return $ " ++ astToHaskellStr val) copts+compile _ (List [Atom "quote", val]) copts = compileScalar (" return $ " ++ ast2Str val) copts  compile env (List [Atom "expand",  _body]) copts = do   -- TODO: check if expand has been rebound?   val <- Language.Scheme.Macro.expand env False _body Language.Scheme.Core.apply-  compileScalar (" return $ " ++ astToHaskellStr val) copts+  compileScalar (" return $ " ++ ast2Str val) copts  compile env (List (Atom "let-syntax" : List _bindings : _body)) copts = do   -- TODO: check if let-syntax has been rebound?@@ -230,9 +235,13 @@   _ <- Language.Scheme.Macro.loadMacros env bodyEnv Nothing False _bindings   -- Expand whole body as a single continuous macro, to ensure hygiene   expanded <- Language.Scheme.Macro.expand bodyEnv False (List _body) Language.Scheme.Core.apply-  case expanded of-    List e -> compile bodyEnv (List $ Atom "begin" : e) copts-    e -> compile bodyEnv e copts+  divertVars bodyEnv expanded copts compexp+ where +   -- Pick up execution here after expansion+   compexp bodyEnv' expanded' copts' = do+     case expanded' of+       List e -> compile bodyEnv' (List $ Atom "begin" : e) copts'+       e -> compile bodyEnv' e copts'  compile env (List (Atom "letrec-syntax" : List _bindings : _body)) copts = do   -- TODO: check if let-syntax has been rebound?@@ -240,33 +249,45 @@   _ <- Language.Scheme.Macro.loadMacros bodyEnv bodyEnv Nothing False _bindings   -- Expand whole body as a single continuous macro, to ensure hygiene   expanded <- Language.Scheme.Macro.expand bodyEnv False (List _body) Language.Scheme.Core.apply-  case expanded of-    List e -> compile bodyEnv (List $ Atom "begin" : e) copts-    e -> compile bodyEnv e copts+  divertVars bodyEnv expanded copts compexp+ where +   -- Pick up execution here after expansion+   compexp bodyEnv' expanded' copts' = do+     case expanded' of+       List e -> compile bodyEnv' (List $ Atom "begin" : e) copts'+       e -> compile bodyEnv' e copts'  compile env (List [Atom "define-syntax", Atom keyword,   (List [Atom "er-macro-transformer",      (List (Atom "lambda" : List fparams : fbody))])])   copts = do+  let fparamsStr = asts2Str fparams+      fbodyStr = asts2Str fbody -  -- TODO: same as below, these defs will eventually need to -  -- be made available during runtime as well as compile time   f <- makeNormalFunc env fparams fbody    _ <- defineNamespacedVar env macroNamespace keyword $ SyntaxExplicitRenaming f-  compileScalar ("  return $ Nil \"\"") copts  -compile env (List [Atom "define-syntax", Atom keyword, (List (Atom "syntax-rules" : (List identifiers : rules)))]) copts = do------ TODO:------ macros will eventually need to introduce a definition in both the compiler's env (so macros can be processed at compile time) and in the program's env (so dynamically injected code has access to the macro). That said, the priority is compile-time processing.------ TBD: how the heck to serialize a Syntax object for use in the compiled code, since---    Syntax has embedded env's... perhaps the first step is to do it with a null env----  _ <- defineNamespacedVar env macroNamespace keyword $ Syntax (Just env) Nothing False identifiers rules-  compileScalar ("  return $ Nil \"\"") copts +  compFunc <- return $ [+    AstValue $ "  f <- makeNormalFunc env " ++ fparamsStr ++ " " ++ fbodyStr, +    AstValue $ "  defineNamespacedVar env macroNamespace \"" ++ keyword ++ "\" $ SyntaxExplicitRenaming f",+    createAstCont copts "(Nil \"\")" ""]+  return $ [createAstFunc copts compFunc] +compile env lisp@(List [Atom "define-syntax", Atom keyword, +    (List (Atom "syntax-rules" : (List identifiers : rules)))]) copts = do+  let idStr = asts2Str identifiers+      ruleStr = asts2Str rules++  -- Make macro available at compile time+  _ <- defineNamespacedVar env macroNamespace keyword $ +         Syntax (Just env) Nothing False identifiers rules++  -- And load it at runtime as well+  -- Env should be identical to the one loaded at compile time...+  compileScalar +    ("  defineNamespacedVar env macroNamespace \"" ++ keyword ++ +     "\" $ Syntax (Just env) Nothing False " ++ idStr ++ " " ++ ruleStr) copts + compile env (List [Atom "if", predic, conseq]) copts =   compile env (List [Atom "if", predic, conseq, Nil ""]) copts @@ -370,7 +391,7 @@  f <- return $ [AstValue $ "  bound <- liftIO $ isRecBound env \"define\"",        AstValue $ "  if bound ",        AstValue $ "     then throwError $ NotImplemented \"prepareApply env cont args\" ", -- if is bound to a variable in this scope; call into it-       AstValue $ "     else do result <- makeHVarargs (" ++ astToHaskellStr varargs ++ ") env (" ++ compiledParams ++ ") " ++ symCallfunc,+       AstValue $ "     else do result <- makeHVarargs (" ++ ast2Str varargs ++ ") env (" ++ compiledParams ++ ") " ++ symCallfunc,        AstValue $ "             _ <- defineVar env \"" ++ var ++ "\" result ",        createAstCont copts "result" "           "        ]@@ -411,7 +432,7 @@  f <- return $ [AstValue $ "  bound <- liftIO $ isRecBound env \"lambda\"",        AstValue $ "  if bound ",        AstValue $ "     then throwError $ NotImplemented \"prepareApply env cont args\" ", -- if is bound to a variable in this scope; call into it-       AstValue $ "     else do result <- makeHVarargs (" ++ astToHaskellStr varargs ++ ") env (" ++ compiledParams ++ ") " ++ symCallfunc,+       AstValue $ "     else do result <- makeHVarargs (" ++ ast2Str varargs ++ ") env (" ++ compiledParams ++ ") " ++ symCallfunc,        createAstCont copts "result" "           "        ]  return $ [createAstFunc copts f] ++ compiledBody@@ -428,7 +449,7 @@  f <- return $ [AstValue $ "  bound <- liftIO $ isRecBound env \"lambda\"",        AstValue $ "  if bound ",        AstValue $ "     then throwError $ NotImplemented \"prepareApply env cont args\" ", -- if is bound to a variable in this scope; call into it-       AstValue $ "     else do result <- makeHVarargs (" ++ astToHaskellStr varargs ++ ") env [] " ++ symCallfunc,+       AstValue $ "     else do result <- makeHVarargs (" ++ ast2Str varargs ++ ") env [] " ++ symCallfunc,        createAstCont copts "result" "           "        ]  return $ [createAstFunc copts f] ++ compiledBody@@ -443,7 +464,7 @@     AstValue $ "  derefValue <- recDerefPtrs tmp",     -- TODO: not entirely correct below; should compile the character argument rather     --       than directly inserting it into the compiled code...-    AstValue $ "  result <- substr (derefValue, (" ++ astToHaskellStr(character) ++ "), idx)",+    AstValue $ "  result <- substr (derefValue, (" ++ ast2Str(character) ++ "), idx)",     AstValue $ "  _ <- updateObject env \"" ++ var ++ "\" result",     createAstCont copts "result" ""]  return $ [entryPt] ++ compDefine ++ [compMakeDefine]@@ -656,8 +677,52 @@ mcompile env lisp copts = mfunc env lisp compile copts mfunc :: Env -> LispVal -> (Env -> LispVal -> CompOpts -> IOThrowsError [HaskAST]) -> CompOpts -> IOThrowsError [HaskAST]  mfunc env lisp func copts = do-  transformed <- Language.Scheme.Macro.macroEval env lisp Language.Scheme.Core.apply-  func env transformed copts+  expanded <- Language.Scheme.Macro.macroEval env lisp Language.Scheme.Core.apply+  divertVars env expanded copts func++-- |Do the actual insertion of diverted variables back to the +--  compiled program.+divertVars +    :: Env +    -- ^ Current compile Environment+    -> LispVal+    -- ^ Lisp code after macro expansion+    -> CompOpts+    -- ^ Compiler options+    -> (Env -> LispVal -> CompOpts -> IOThrowsError [HaskAST])+    -- ^ Continuation to call into after vars are diverted+    -> IOThrowsError [HaskAST]+    -- ^ Code generated by the continuation, along with the code+    --   added to divert vars to the compiled program+divertVars env expanded copts@(CompileOptions tfnc uvar uargs nfnc) func = do+  vars <- Language.Scheme.Macro.getDivertedVars env+  case vars of +    [] -> func env expanded copts+    _ -> do +      Atom symNext <- _gensym "afterDivert"+      diverted <- compileDivertedVars symNext env vars copts+      rest <- func env expanded $ CompileOptions symNext uvar uargs nfnc+      return $ [diverted] ++ rest++-- |Take a list of variables diverted into env at compile time, and+--  divert them into the env at runtime+compileDivertedVars :: String -> Env -> [LispVal] -> CompOpts -> IOThrowsError HaskAST+compileDivertedVars +  formNext env vars +  copts@(CompileOptions thisFunc useVal useArgs nextFunc) = do+  let val = case useVal of+        True -> "value"+        _ -> "Nil \"\""+      args = case useArgs of+        True -> "(Just args)"+        _ -> "Nothing"+      comp (List [Atom renamed, Atom orig]) = do+        [AstValue $ "  v <- getVar env \"" ++ orig ++ "\"",+         AstValue $ "  _ <- defineVar env \"" ++ renamed ++ "\" v"]+      cvars = map comp vars +      f = (concat cvars) ++ +          [AstValue $ "  " ++ formNext ++ " env cont (" ++ val ++ ") " ++ args]+  return $ createAstFunc copts f  {- TODO: adapt for compilation meval, mprepareApply :: Env -> LispVal -> LispVal -> IOThrowsError LispVal
hs-src/Language/Scheme/Core.hs view
@@ -24,6 +24,7 @@     , primitiveBindings     , version     -- * Utility functions+    , registerExtensions     , escapeBackslashes     , showBanner     , substr@@ -48,7 +49,7 @@  -- |husk version number version :: String-version = "3.6.1"+version = "3.6.2"  -- |A utility function to display the husk console banner showBanner :: IO ()@@ -65,6 +66,21 @@   putStrLn $ " Version " ++ version ++ " "   putStrLn "                                                                         " +-- |Register optional SRFI extensions+registerExtensions :: Env -> (FilePath -> IO FilePath) -> IO ()+registerExtensions env getDataFileName = do+  _ <- registerSRFI env getDataFileName 1+  _ <- registerSRFI env getDataFileName 2+  return ()++-- |Register the given SRFI+registerSRFI :: Env -> (FilePath -> IO FilePath) -> Integer -> IO ()+registerSRFI env getDataFileName num = do+ filename <- getDataFileName $ "lib/srfi/srfi-" ++ show num ++ ".scm"+ _ <- evalString env $ "(register-extension '(srfi " ++ show num ++ ") \"" ++ +  (escapeBackslashes filename) ++ "\")"+ return ()+ -- |A utility function to escape backslashes in the given string escapeBackslashes :: String -> String escapeBackslashes s = foldr step [] s@@ -236,94 +252,6 @@ -- Quote an expression by simply passing along the value eval env cont (List [Atom "quote", val]) = continueEval env cont val -{- Unquote an expression; unquoting is different than quoting in that-it may also be inter-spliced with code that is meant to be evaluated. -}-------{- FUTURE: Issue #8 - https://github.com/justinethier/husk-scheme/issues/#issue/8-need to take nesting of ` into account, as per spec: -}----{- - Quasiquote forms may be nested.-- Substitutions are made only for unquoted components appearing at the-same nesting level as the outermost backquote.-- The nesting level increases by one inside each successive quasiquotation,-and decreases by one inside each unquotation. -}----{- So the upshoot is that a new nesting level var needs to be threaded through,-and used to determine whether or not to evaluate an unquote. -}----eval envi cont (List [Atom "quasiquote", value]) = cpsUnquote envi cont value Nothing-  where cpsUnquote :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal-        cpsUnquote e c val _ = do-          case val of-            List [Atom "unquote", vval] -> meval e c vval-            List (_ : _) -> doCpsUnquoteList e c val-            DottedList xs x -> do-              doCpsUnquoteList e (makeCPSWArgs e c cpsUnquotePair $ [x] ) $ List xs-            Vector vec -> do-              let len = length (elems vec)-              if len > 0-                 then doCpsUnquoteList e (makeCPS e c cpsUnquoteVector) $ List $ elems vec-                 else continueEval e c $ Vector $ listArray (0, -1) []-            _ -> meval e c (List [Atom "quote", val])  -- Behave like quote if there is nothing to "unquote"...--        {- Unquote a pair-        This must be started by unquoting the "left" hand side of the pair,-        then pass a continuation to this function to unquote the right-hand side (RHS).-        This function does the RHS and then calls into a continuation to finish the pair. -}-        cpsUnquotePair :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal-        cpsUnquotePair e c (List rxs) (Just [rx]) = do-          cpsUnquote e (makeCPSWArgs e c cpsUnquotePairFinish $ [List rxs]) rx Nothing-        cpsUnquotePair _ _ _ _ = throwError $ InternalError "Unexpected parameters to cpsUnquotePair"--        -- Finish unquoting a pair by combining both of the unquoted left/right hand sides.-        cpsUnquotePairFinish :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal-        cpsUnquotePairFinish e c _rx (Just [List rxs]) = do-            rx <- recDerefPtrs _rx-            case rx of-              List [] -> continueEval e c $ List rxs-              List rxlst -> continueEval e c $ List $ rxs ++ rxlst-              DottedList rxlst rxlast -> continueEval e c $ DottedList (rxs ++ rxlst) rxlast-              _ -> continueEval e c $ DottedList rxs rx-        cpsUnquotePairFinish _ _ _ _ = throwError $ InternalError "Unexpected parameters to cpsUnquotePairFinish"--        -- Unquote a vector-        cpsUnquoteVector :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal-        cpsUnquoteVector e c (List vList) _ = continueEval e c (Vector $ listArray (0, (length vList - 1)) vList)-        cpsUnquoteVector _ _ _ _ = throwError $ InternalError "Unexpected parameters to cpsUnquoteVector"--        -- Front-end to cpsUnquoteList, to encapsulate default values in the call-        doCpsUnquoteList :: Env -> LispVal -> LispVal -> IOThrowsError LispVal-        doCpsUnquoteList e c (List (x : xs)) = cpsUnquoteList e c x $ Just ([List xs, List []])-        doCpsUnquoteList _ _ _ = throwError $ InternalError "Unexpected parameters to doCpsUnquoteList"--        -- Unquote a list-        cpsUnquoteList :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal-        cpsUnquoteList e c val (Just ([List unEvaled, List acc])) = do-            case val of-                List [Atom "unquote-splicing", vvar] -> do-                    meval e (makeCPSWArgs e c cpsUnquoteSplicing $ [List unEvaled, List acc]) vvar-                _ -> cpsUnquote e (makeCPSWArgs e c cpsUnquoteFld $ [List unEvaled, List acc]) val Nothing-        cpsUnquoteList _ _ _ _ = throwError $ InternalError "Unexpected parameters to cpsUnquoteList"--        -- Evaluate an expression instead of quoting it-        cpsUnquoteSplicing :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal-        cpsUnquoteSplicing e c val (Just ([List unEvaled, List acc])) = do-                    case val of-                        List v -> case unEvaled of-                                    [] -> continueEval e c $ List $ acc ++ v-                                    _ -> cpsUnquoteList e c (head unEvaled) (Just [List (tail unEvaled), List $ acc ++ v ])-                        _ -> throwError $ TypeMismatch "proper list" val-        cpsUnquoteSplicing _ _ _ _ = throwError $ InternalError "Unexpected parameters to cpsUnquoteSplicing"--        -- Unquote processing for single field of a list-        cpsUnquoteFld :: Env -> LispVal -> LispVal -> Maybe [LispVal] -> IOThrowsError LispVal-        cpsUnquoteFld e c val (Just ([List unEvaled, List acc])) = do-          case unEvaled of-            [] -> continueEval e c $ List $ acc ++ [val]-            _ -> cpsUnquoteList e c (head unEvaled) (Just [List (tail unEvaled), List $ acc ++ [val] ])-        cpsUnquoteFld _ _ _ _ = throwError $ InternalError "Unexpected parameters to cpsUnquoteFld"- -- A special form to assist with debugging macros eval env cont args@(List [Atom "expand" , _body]) = do  bound <- liftIO $ isRecBound env "expand"@@ -752,7 +680,8 @@ apply cont (EvalFunc func) args = do     {- An EvalFunc extends the evaluator so it needs access to the current continuation;     pass it as the first argument. -}-    func (cont : args)+  List dargs <- recDerefPtrs $ List args -- Deref any pointers+  func (cont : dargs) apply cont (PrimitiveFunc func) args = do   List dargs <- recDerefPtrs $ List args -- Deref any pointers   result <- liftThrows $ func dargs@@ -814,7 +743,10 @@         bindVarArgs arg env = case arg of           Just argName -> liftIO $ extendEnv env [((varNamespace, argName), List $ remainingArgs)]           Nothing -> return env-apply _ func args = throwError $ BadSpecialForm "Unable to evaluate form" $ List (func : args)+apply _ func args = do+  List [func'] <- recDerefPtrs $ List [func] -- Deref any pointers+  List args' <- recDerefPtrs $ List args+  throwError $ BadSpecialForm "Unable to evaluate form" $ List (func' : args')  {- |Environment containing the primitive forms that are built into the Scheme language. Note that this only includes forms that are implemented in Haskell; derived forms implemented in Scheme (such as let, list, etc) are available@@ -830,7 +762,9 @@ {- These functions have access to the current environment via the current continuation, which is passed as the first LispVal argument. -} ---evalfuncExitSuccess, evalfuncExitFail, evalfuncApply, evalfuncDynamicWind, evalfuncEval, evalfuncLoad, evalfuncCallCC, evalfuncCallWValues :: [LispVal] -> IOThrowsError LispVal+evalfuncExitSuccess, evalfuncExitFail, evalfuncApply, evalfuncDynamicWind, +  evalfuncEval, evalfuncLoad, evalfuncCallCC, evalfuncCallWValues,+  evalfuncMakeEnv, evalfuncNullEnv, evalfuncInteractionEnv :: [LispVal] -> IOThrowsError LispVal  {-  - A (somewhat) simplified implementation of dynamic-wind@@ -893,6 +827,23 @@ evalfuncApply (_ : args) = throwError $ NumArgs 2 args -- Skip over continuation argument evalfuncApply _ = throwError $ NumArgs 2 [] ++evalfuncMakeEnv (cont@(Continuation env _ _ _ _) : _) = do+    e <- liftIO $ nullEnv+    continueEval env cont $ LispEnv e++evalfuncNullEnv [cont@(Continuation env _ _ _ _), Number version] = do+    nullEnv <- liftIO $ primitiveBindings+    continueEval env cont $ LispEnv nullEnv+evalfuncNullEnv (_ : args) = throwError $ NumArgs 1 args -- Skip over continuation argument+evalfuncNullEnv _ = throwError $ NumArgs 1 []++evalfuncInteractionEnv (cont@(Continuation env _ _ _ _) : _) = do+    continueEval env cont $ LispEnv env++evalfuncLoad [cont@(Continuation _ a b c d), String filename, LispEnv env] = do+    evalfuncLoad [Continuation env a b c d, String filename]+ evalfuncLoad [cont@(Continuation env _ _ _ _), String filename] = do {- -- It would be nice to use CPS style below.@@ -922,6 +873,7 @@ -- FUTURE: consider allowing env to be specified, per R5RS -- evalfuncEval [cont@(Continuation env _ _ _ _), val] = meval env cont val+evalfuncEval [cont@(Continuation _ _ _ _ _), val, LispEnv env] = meval env cont val evalfuncEval (_ : args) = throwError $ NumArgs 1 args -- Skip over continuation argument evalfuncEval _ = throwError $ NumArgs 1 [] @@ -962,6 +914,11 @@                   , ("dynamic-wind", evalfuncDynamicWind)                   , ("eval", evalfuncEval)                   , ("load", evalfuncLoad)+                  , ("null-environment", evalfuncNullEnv)+                  , ("current-environment", evalfuncInteractionEnv)+                  , ("interaction-environment", evalfuncInteractionEnv)+                  , ("make-environment", evalfuncMakeEnv)+                -- Non-standard extensions #ifdef UseFfi                   , ("load-ffi", Language.Scheme.FFI.evalfuncLoadFFI)
hs-src/Language/Scheme/Macro.hs view
@@ -45,6 +45,7 @@       expand     , macroEval     , loadMacros  +    , getDivertedVars      ) where import Language.Scheme.Types import Language.Scheme.Variables@@ -53,7 +54,7 @@ import Language.Scheme.Primitives (_gensym) import Control.Monad.Error import Data.Array---import Debug.Trace -- Only req'd to support trace, can be disabled at any time...+-- import Debug.Trace -- Only req'd to support trace, can be disabled at any time...  {-  Implementation notes:@@ -101,6 +102,19 @@ --needToExtendEnv (List [Atom "define-syntax", Atom _, (List (Atom "syntax-rules" : (List _ : _)))]) = True --needToExtendEnv _ = False  +-- |Get a list of variables that the macro hygiene +--  subsystem diverted back into the calling environment.+--+--  This is a specialized function that is only+--  mean to be used by the husk compiler.+getDivertedVars :: Env -> IOThrowsError [LispVal]+getDivertedVars env = do+  List tmp <- getNamespacedVar env " " "diverted"+  return tmp++clearDivertedVars :: Env -> IOThrowsError LispVal+clearDivertedVars env = defineNamespacedVar env " " "diverted" $ List []+ -- |Examines the input AST to see if it is a macro call.  --  If a macro call is found, the code is expanded. --  Otherwise the input is returned unchanged.@@ -121,6 +135,13 @@  -  -} macroEval env lisp@(List (Atom x : _)) apply = do+  -- Keep track of diverted variables+  _ <- clearDivertedVars env+  _macroEval env lisp apply+macroEval env lisp apply = _macroEval env lisp apply++-- |Do the actual work for the 'macroEval' wrapper func+_macroEval env lisp@(List (Atom x : _)) apply = do   -- Note: If there is a procedure of the same name it will be shadowed by the macro.   isDefined <- liftIO $ isNamespacedRecBound env macroNamespace x   if isDefined@@ -132,7 +153,7 @@            renameEnv <- liftIO $ nullEnv -- Local environment used just for this            expanded <- explicitRenamingTransform env renameEnv                                                 lisp transformer apply-           macroEval env expanded apply+           _macroEval env expanded apply           -- Syntax Rules          Syntax (Just defEnv) _ definedInMacro identifiers rules -> do@@ -149,13 +170,13 @@            expanded <- macroTransform defEnv env env renameEnv cleanupEnv                                        definedInMacro                                       (List identifiers) rules lisp apply-           macroEval env expanded apply+           _macroEval env expanded apply            -- Useful debug to see all exp's:            -- macroEval env (trace ("exp = " ++ show expanded) expanded)      else return lisp  -- No macro to process, just return code as it is...-macroEval _ lisp@(_) _ = return lisp+_macroEval _ lisp@(_) _ = return lisp  {-  - Given input and syntax-rules, determine if any rule is a match and transform it.@@ -600,6 +621,8 @@ -- function parameter instead of the Syntax object -- +  -- Keep track of diverted variables+  _ <- clearDivertedVars env   walkExpanded env env env renameEnv cleanupEnv dim True False (List []) code apply  -- |Walk expanded code per Clinger's algorithm from Macros That Work@@ -634,7 +657,7 @@   -- If a macro is quoted, keep track of it and do not invoke rules below for  -- procedure abstraction or macro calls - let isQuoted = inputIsQuoted || (a == "quote") || (a == "quasiquote")+ let isQuoted = inputIsQuoted || (a == "quote")   isDefinedAsMacro <- liftIO $ isNamespacedRecBound useEnv macroNamespace a @@ -660,6 +683,14 @@      || a == "define"        || a == "set!"      || a == "lambda"+     || a == "quote"+     || a == "expand"+     || a == "string-set!"+     || a == "set-car!"+     || a == "set-cdr!"+     || a == "vector-set!"+     || a == "hash-table-set!"+     || a == "hash-table-delete!"     then walkExpandedAtom defEnv useEnv divertEnv renameEnv cleanupEnv                            dim startOfList inputIsQuoted (List result) a ts isQuoted isDefinedAsMacro apply     else walkExpanded defEnv useEnv divertEnv renameEnv cleanupEnv @@ -857,7 +888,7 @@          -- I am still concerned that this may highlight a flaw in the husk          -- implementation, and that this solution may not be complete.          ---         List lexpanded <- cleanExpanded defEnv useEnv divertEnv renameEnv renameEnv True False False (List []) (List ts) apply+         List lexpanded <- cleanExpanded defEnv useEnv divertEnv renameEnv renameEnv True False (List []) (List ts) apply          macroTransform defEnv useEnv divertEnv renameClosure cleanupEnv definedInMacro (List identifiers) rules (List (Atom a : lexpanded)) apply       Syntax (Just _defEnv) _ definedInMacro identifiers rules -> do          macroTransform _defEnv useEnv divertEnv renameEnv cleanupEnv definedInMacro (List identifiers) rules (List (Atom a : ts)) apply@@ -887,11 +918,10 @@     a     ts     True _ apply = do-    let isQuasiQuoted = (a == "quasiquote")     -- Cleanup all symbols in the quoted code     List cleaned <- cleanExpanded                        defEnv useEnv divertEnv renameEnv cleanupEnv -                      dim True isQuasiQuoted +                      dim True                       (List []) (List ts)                       apply     return $ List $ result ++ (Atom a : cleaned)@@ -917,18 +947,27 @@ markBoundIdentifiers env cleanupEnv (_: vs) renamedVars = markBoundIdentifiers env cleanupEnv vs renamedVars markBoundIdentifiers _ _ [] renamedVars = return $ List renamedVars --- |Recursively expand an atom that may have been renamed multiple times-expandAtom :: Env -> LispVal -> IOThrowsError LispVal-expandAtom renameEnv (Atom a) = do+-- |Expand an atom, optionally recursively+_expandAtom :: Bool -> Env -> LispVal -> IOThrowsError LispVal+_expandAtom isRec renameEnv (Atom a) = do   isDefined <- liftIO $ isRecBound renameEnv a -- Search parent Env's also   if isDefined       then do        expanded <- getVar renameEnv a---       return (trace ("ea renaming " ++ a ++ " to " ++ show expanded) expanded) -- disabled this; just expand once. expandAtom renameEnv expanded -- Recursively expand-       return expanded -- disabled this; just expand once. expandAtom renameEnv expanded -- Recursively expand+       case isRec of+         True  -> _expandAtom isRec renameEnv expanded+         False -> return expanded      else return $ Atom a -expandAtom _ a = return a+_expandAtom _ _ a = return a +-- |Recursively expand an atom that may have been renamed multiple times+recExpandAtom :: Env -> LispVal -> IOThrowsError LispVal+recExpandAtom renameEnv a = _expandAtom True renameEnv a++-- |Expand an atom+expandAtom :: Env -> LispVal -> IOThrowsError LispVal+expandAtom renameEnv a = _expandAtom False renameEnv a+ -- |Clean up any remaining renamed variables in the expanded code --  Only needed in special circumstances to deal with quoting. --@@ -949,63 +988,39 @@   -> Env    -> Bool    -> Bool -  -> Bool    -> LispVal    -> LispVal    -> (LispVal -> LispVal -> [LispVal] -> IOThrowsError LispVal) -- ^Apply func   -> IOThrowsError LispVal -cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ isQQ (List result) (List (List l : ls)) apply = do-  lst <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True isQQ (List []) (List l) apply-  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False isQQ (List $ result ++ [lst]) (List ls) apply+cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ (List result) (List (List l : ls)) apply = do+  lst <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True (List []) (List l) apply+  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False (List $ result ++ [lst]) (List ls) apply -cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ isQQ (List result) (List ((Vector v) : vs)) apply = do-  List lst <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True isQQ (List []) (List $ elems v) apply-  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False isQQ (List $ result ++ [asVector lst]) (List vs) apply+cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ (List result) (List ((Vector v) : vs)) apply = do+  List lst <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True (List []) (List $ elems v) apply+  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False (List $ result ++ [asVector lst]) (List vs) apply -cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ isQQ (List result) (List ((DottedList ds d) : ts)) apply = do-  List ls <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True isQQ (List []) (List ds) apply-  l <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True isQQ (List []) d apply-  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False isQQ (List $ result ++ [DottedList ls l]) (List ts) apply+cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ (List result) (List ((DottedList ds d) : ts)) apply = do+  List ls <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True (List []) (List ds) apply+  l <- cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True (List []) d apply+  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False (List $ result ++ [DottedList ls l]) (List ts) apply -cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim startOfList isQQ (List result) (List (Atom a : ts)) apply = do-  expanded <- tmpexpandAtom cleanupEnv $ Atom a-  case (startOfList, isQQ, expanded) of-    -- Unquote an expression by continuing to expand it as a macro form-    -- -    -- Only perform an unquote if (in order):-    --  - We are currently at the head of the list-    --  - Expression is quasi-quoted-    --  - An "unquote" is found-    ---    (True, True, Atom "unquote") -> do -        r <- walkExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim True False (List $ result ++ [Atom "unquote"]) (List ts) apply-        return r-    _ -> -        cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False isQQ (List $ result ++ [expanded]) (List ts) apply- where-  -- TODO: figure out a way to simplify this code (perhaps consolidate with expandAtom)-  tmpexpandAtom :: Env -> LispVal -> IOThrowsError LispVal-  tmpexpandAtom _renameEnv (Atom _a) = do-    isDefined <- liftIO $ isRecBound _renameEnv _a -- Search parent Env's also-    if isDefined -       then do-         expanded <- getVar _renameEnv _a-         tmpexpandAtom _renameEnv expanded -- Recursively expand-       else return $ Atom _a -  tmpexpandAtom _ _a = return _a+cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim startOfList (List result) (List (Atom a : ts)) apply = do+  expanded <- recExpandAtom cleanupEnv $ Atom a+  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False (List $ result ++ [expanded]) (List ts) apply  -- Transform anything else as itself...-cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ isQQ (List result) (List (t : ts)) apply = do-  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False isQQ (List $ result ++ [t]) (List ts) apply+cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim _ (List result) (List (t : ts)) apply = do+  cleanExpanded defEnv useEnv divertEnv renameEnv cleanupEnv dim False (List $ result ++ [t]) (List ts) apply  -- Base case - empty transform-cleanExpanded _ _ _ _ _ _ _ _ result@(List _) (List []) _ = do+cleanExpanded _ _ _ _ _ _ _ result@(List _) (List []) _ = do   return result  -- If transforming into a scalar, just return the transform directly... -- Not sure if this is strictly desirable, but does not break any tests so we'll go with it for now.-cleanExpanded _ _ _ _ _ _ _ _ _ transform _ = return transform+cleanExpanded _ _ _ _ _ _ _ _ transform _ = return transform   {- |Transform input by walking the tranform structure and creating a new structure@@ -1293,11 +1308,9 @@  -- |A helper function for transforming an atom that has been marked as as literal identifier transformLiteralIdentifier :: Env -> Env -> Env -> Env -> Bool -> String -> IOThrowsError LispVal-transformLiteralIdentifier defEnv _ divertEnv renameEnv definedInMacro transform = do+transformLiteralIdentifier defEnv outerEnv divertEnv renameEnv definedInMacro transform = do   isInDef <- liftIO $ isRecBound defEnv transform   isRenamed <- liftIO $ isRecBound renameEnv transform---  if (trace ("a = " ++ transform ++ " inDef = " ++ show isInDef ++ " isRnm = " ++ show isRenamed ++ " dim = " ++ show definedInMacro) isInDef) && not isRenamed---  TODO: isRenamed should only matter if the macro was originally defined within another macro   if (isInDef && not definedInMacro) || (isInDef && definedInMacro && not isRenamed)      then do           {- Variable exists in the environment the macro was defined in,@@ -1307,29 +1320,36 @@          value <- getVar defEnv transform          Atom renamed <- _gensym transform          _ <- defineVar divertEnv renamed value ---         return $ Atom (trace ("diverted " ++ transform ++ " into " ++ renamed) renamed)-         return $ Atom renamed-     else do-{- TODO:         -else if not defined in defEnv then just pass the var back as-is (?)-  this is not entirely correct, a special form would not be defined but still has-  a meaning and could be shadowed in useEnv. need some way of being able to-  divert a special form back into useEnv... -Or, consider the following example. csi throws an error because if is not defined.-If we make the modifications to store intermediate vars somewhere that are introduced-via lambda, set!, and define then we may be able to throw an error if the var is not-defined, instead of trying to store the special form to a variable somehow.+-- TODO: this is temporary testing code+         List diverted <- getNamespacedVar outerEnv " " "diverted"+         _ <- setNamespacedVar outerEnv " " "diverted" $ +             List (diverted ++ [List [Atom renamed, Atom transform]])+-- END -;(define if 3)-(define-syntax test-template- (syntax-rules (if)-    ((_)-        if)))-(write (let ((if 1)) (test-template)) )-(write (let ((if 2)) (test-template)) )--}+         return $ Atom renamed+     else do+         -- else if not defined in defEnv then just pass the var back as-is          return $ Atom transform+         {-+           TODO:         +           above "else" is not entirely correct, a special form would not be defined but still+           has a meaning and could be shadowed in useEnv. need some way of being able to+           divert a special form back into useEnv...+         +           Or, consider the following example. csi throws an error because if is not defined.+           If we make the modifications to store intermediate vars somewhere that are introduced+           via lambda, set!, and define then we may be able to throw an error if the var is not+           defined, instead of trying to store the special form to a variable somehow.+           +           ;(define if 3)+           (define-syntax test-template+            (syntax-rules (if)+               ((_)+                   if)))+           (write (let ((if 1)) (test-template)) )+           (write (let ((if 2)) (test-template)) )+         -}  -- | A helper function for transforming an improper list transformDottedList :: Env -> Env -> Env -> Env -> Env -> Env -> Bool -> LispVal -> Int -> [Int] -> LispVal -> LispVal -> IOThrowsError LispVal@@ -1365,7 +1385,7 @@    buildTransformedCode results ps p = do       case p of         [List []] -> List $ results ++ [List ps]         -- Proper list has null list at the end-        [List l@(Atom "unquote" : _ )] -> List $ results ++ [DottedList ps $ List l] -- Special case from parser. +--        [List l@(Atom "unquote" : _ )] -> List $ results ++ [DottedList ps $ List l] -- Special case from parser.          [List ls] -> List $ results ++ [List $ ps ++ ls] -- Again, convert to proper list because a proper list is at end         [l] -> List $ results ++ [DottedList ps l]         ls -> do
hs-src/Language/Scheme/Macro/ExplicitRenaming.hs view
@@ -40,12 +40,13 @@ explicitRenamingTransform useEnv renameEnv lisp                            transformer@(Func _ _ _ defEnv) apply = do   let continuation = makeNullContinuation useEnv-  apply +  result <- apply      continuation     transformer     [lisp,       IOFunc $ exRename useEnv renameEnv defEnv,       IOFunc $ exCompare useEnv renameEnv defEnv] +  recDerefPtrs result  -- |The explicit renaming "rename" function --@@ -82,6 +83,13 @@             Atom renamed <- _gensym a -- Unique name             _ <- defineVar useEnv renamed value -- divert value to Use Env             _ <- defineVar renameEnv a $ Atom renamed -- Record renamed sym++-- TODO: this is temporary testing code+            List diverted <- getNamespacedVar useEnv " " "diverted"+            _ <- setNamespacedVar useEnv " " "diverted" $ +                List (diverted ++ [List [Atom renamed, Atom a]])+-- END+             return $ Atom renamed      else        return $ Atom a
hs-src/Language/Scheme/Parser.hs view
@@ -374,10 +374,10 @@          x <- parseVector          _ <- lexeme $ char ')'          return x-  <|> do _ <- try (lexeme $ string "#hash(")-         x <- parseHashTable-         _ <- lexeme $ char ')'-         return x+--  <|> do _ <- try (lexeme $ string "#hash(")+--         x <- parseHashTable+--         _ <- lexeme $ char ')'+--         return x   <|> try (parseAtom)   <|> lexeme parseString   <|> lexeme parseBool
hs-src/Language/Scheme/Types.hs view
@@ -68,6 +68,7 @@              , synIdentifiers              , synRules         , SyntaxExplicitRenaming+        , LispEnv         , EOF         , Nil)     , toOpaque@@ -258,6 +259,7 @@      --   manipulated by the same functions as regular variables.  | SyntaxExplicitRenaming LispVal    -- ^ Syntax for an explicit-renaming macro+ | LispEnv Env  | EOF  | Nil String  -- ^Internal use only; do not use this type directly.@@ -392,6 +394,7 @@ showVal :: LispVal -> String showVal (Nil _) = "" showVal (EOF) = "#!EOF"+showVal (LispEnv _) = "<env>" showVal (String contents) = "\"" ++ contents ++ "\"" showVal (Char chr) = [chr] showVal (Atom name) = name
husk-scheme.cabal view
@@ -1,9 +1,32 @@ Name:                husk-scheme-Version:             3.6.1+Version:             3.6.2 Synopsis:            R5RS Scheme interpreter, compiler, and library.-Description:         A dialect of R5RS Scheme written in Haskell. Provides advanced -                     features including continuations, hygienic macros, a Haskell FFI,-                     and the full numeric tower.+Description:         +  <<https://github.com/justinethier/husk-scheme/raw/master/docs/husk-scheme.png>>+  .+  A dialect of R5RS Scheme written in Haskell. Advanced +  features are provided including:+  .+  * First-class continuations of unlimited extent+  .+  * Hygienic macros based on syntax-rules+  .+  * Low-level explicit renaming macros +  .+  * A foreign function interface (FFI) to Haskell+  .+  * Full numeric tower providing support for real, rational, and complex numbers+  .+  * Proper tail recursion and lexical scoping+  .+  * Read-Eval-Print-Loop (REPL) interpreter, with input driven by Haskeline to provide a rich user experience+  .+  * Standard library of Scheme functions, and support for many popular SRFI's+  .+  Husk may be used as either a stand-alone interpreter or as an extension language within a larger Haskell application. By closely following the R5RS standard, the intent is to develop a Scheme that is as compatible as possible with other R5RS Schemes. Husk is mature enough for use in production applications, however it is not optimized for performance-critical applications. +  .+  Scheme is one of two main dialects of Lisp. Scheme follows a minimalist design philosophy: the core language consists of a small number of fundamental forms which may be used to implement other built-in forms. Scheme is an excellent language for writing small, elegant programs, and may also be used to write scripts or embed scripting functionality within a larger application.+ License:             MIT License-file:        LICENSE Author:              Justin Ethier@@ -18,7 +41,7 @@                      ChangeLog.markdown                      LICENSE                      AUTHORS-Data-Files:          stdlib.scm srfi/srfi-1.scm srfi/srfi-55.scm+Data-Files:          lib/stdlib.scm lib/srfi/*.scm  Source-Repository head     Type:            git
+ lib/srfi/srfi-1.scm view
@@ -0,0 +1,1664 @@+;;;+;;;+;;; husk-scheme+;;; http://justinethier.github.com/husk-scheme+;;;+;;;+;;;+;;; Shiver's reference implementation of SRFI 1 from +;;; http://srfi.schemers.org/srfi-1/srfi-1-reference.scm+;;; Copyright (c) 1998, 1999 by Olin Shivers.+;;;+;;;+;;; Implementation of SRFI-1: List Library+;;;+;;; This file contains all of the SRFI-1 functions+;;; that are not part of the standard library.+;;;+++;;; SRFI-1 list-processing library 			-*- Scheme -*-+;;; Reference implementation+;;;+;;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with+;;; this code as long as you do not remove this copyright notice or+;;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.+;;;     -Olin++;;; This is a library of list- and pair-processing functions. I wrote it after+;;; carefully considering the functions provided by the libraries found in+;;; R4RS/R5RS Scheme, MIT Scheme, Gambit, RScheme, MzScheme, slib, Common+;;; Lisp, Bigloo, guile, T, APL and the SML standard basis. It is a pretty+;;; rich toolkit, providing a superset of the functionality found in any of+;;; the various Schemes I considered.++;;; This implementation is intended as a portable reference implementation+;;; for SRFI-1. See the porting notes below for more information.++;;; Exported:+;;; xcons tree-copy make-list list-tabulate cons* list-copy +;;; proper-list? circular-list? dotted-list? not-pair? null-list? list=+;;; circular-list length++;;; iota+;;; first second third fourth fifth sixth seventh eighth ninth tenth+;;; car+cdr+;;; take       drop       +;;; take-right drop-right +;;; take!      drop-right!+;;; split-at   split-at!+;;; last last-pair+;;; zip unzip1 unzip2 unzip3 unzip4 unzip5+;;; count+;;; append! append-reverse append-reverse! concatenate concatenate! +;;; unfold       fold       pair-fold       reduce+;;; unfold-right fold-right pair-fold-right reduce-right+;;; append-map append-map! map! pair-for-each filter-map map-in-order+;;; filter  partition  remove+;;; filter! partition! remove! +;;; find find-tail any every list-index+;;; take-while drop-while take-while!+;;; span break span! break!+;;; delete delete!+;;; alist-cons alist-copy+;;; delete-duplicates delete-duplicates!+;;; alist-delete alist-delete!+;;; reverse! +;;; lset<= lset= lset-adjoin  +;;; lset-union  lset-intersection  lset-difference  lset-xor  lset-diff+intersection+;;; lset-union! lset-intersection! lset-difference! lset-xor! lset-diff+intersection!+;;; +;;; In principle, the following R4RS list- and pair-processing procedures+;;; are also part of this package's exports, although they are not defined+;;; in this file:+;;;   Primitives: cons pair? null? car cdr set-car! set-cdr!+;;;   Non-primitives: list length append reverse cadr ... cddddr list-ref+;;;                   memq memv assq assv+;;;   (The non-primitives are defined in this file, but commented out.)+;;;+;;; These R4RS procedures have extended definitions in SRFI-1 and are defined+;;; in this file:+;;;   map for-each member assoc+;;;+;;; The remaining two R4RS list-processing procedures are not included: +;;;   list-tail (use drop)+;;;   list? (use proper-list?)+++;;; A note on recursion and iteration/reversal:+;;; Many iterative list-processing algorithms naturally compute the elements+;;; of the answer list in the wrong order (left-to-right or head-to-tail) from+;;; the order needed to cons them into the proper answer (right-to-left, or+;;; tail-then-head). One style or idiom of programming these algorithms, then,+;;; loops, consing up the elements in reverse order, then destructively +;;; reverses the list at the end of the loop. I do not do this. The natural+;;; and efficient way to code these algorithms is recursively. This trades off+;;; intermediate temporary list structure for intermediate temporary stack+;;; structure. In a stack-based system, this improves cache locality and+;;; lightens the load on the GC system. Don't stand on your head to iterate!+;;; Recurse, where natural. Multiple-value returns make this even more+;;; convenient, when the recursion/iteration has multiple state values.++;;; Porting:+;;; This is carefully tuned code; do not modify casually.+;;;   - It is careful to share storage when possible;+;;;   - Side-effecting code tries not to perform redundant writes.+;;; +;;; That said, a port of this library to a specific Scheme system might wish+;;; to tune this code to exploit particulars of the implementation. +;;; The single most important compiler-specific optimisation you could make+;;; to this library would be to add rewrite rules or transforms to:+;;; - transform applications of n-ary procedures (e.g. LIST=, CONS*, APPEND,+;;;   LSET-UNION) into multiple applications of a primitive two-argument +;;;   variant.+;;; - transform applications of the mapping functions (MAP, FOR-EACH, FOLD, +;;;   ANY, EVERY) into open-coded loops. The killer here is that these +;;;   functions are n-ary. Handling the general case is quite inefficient,+;;;   requiring many intermediate data structures to be allocated and+;;;   discarded.+;;; - transform applications of procedures that take optional arguments+;;;   into calls to variants that do not take optional arguments. This+;;;   eliminates unnecessary consing and parsing of the rest parameter.+;;;+;;; These transforms would provide BIG speedups. In particular, the n-ary+;;; mapping functions are particularly slow and cons-intensive, and are good+;;; candidates for tuning. I have coded fast paths for the single-list cases,+;;; but what you really want to do is exploit the fact that the compiler+;;; usually knows how many arguments are being passed to a particular+;;; application of these functions -- they are usually explicitly called, not+;;; passed around as higher-order values. If you can arrange to have your+;;; compiler produce custom code or custom linkages based on the number of+;;; arguments in the call, you can speed these functions up a *lot*. But this+;;; kind of compiler technology no longer exists in the Scheme world as far as+;;; I can see.+;;;+;;; Note that this code is, of course, dependent upon standard bindings for+;;; the R5RS procedures -- i.e., it assumes that the variable CAR is bound+;;; to the procedure that takes the car of a list. If your Scheme +;;; implementation allows user code to alter the bindings of these procedures+;;; in a manner that would be visible to these definitions, then there might+;;; be trouble. You could consider horrible kludgery along the lines of+;;;    (define fact +;;;      (let ((= =) (- -) (* *))+;;;        (letrec ((real-fact (lambda (n) +;;;                              (if (= n 0) 1 (* n (real-fact (- n 1)))))))+;;;          real-fact)))+;;; Or you could consider shifting to a reasonable Scheme system that, say,+;;; has a module system protecting code from this kind of lossage.+;;;+;;; This code does a fair amount of run-time argument checking. If your+;;; Scheme system has a sophisticated compiler that can eliminate redundant+;;; error checks, this is no problem. However, if not, these checks incur+;;; some performance overhead -- and, in a safe Scheme implementation, they+;;; are in some sense redundant: if we don't check to see that the PROC +;;; parameter is a procedure, we'll find out anyway three lines later when+;;; we try to call the value. It's pretty easy to rip all this argument +;;; checking code out if it's inappropriate for your implementation -- just+;;; nuke every call to CHECK-ARG.+;;;+;;; On the other hand, if you *do* have a sophisticated compiler that will+;;; actually perform soft-typing and eliminate redundant checks (Rice's systems+;;; being the only possible candidate of which I'm aware), leaving these checks +;;; in can *help*, since their presence can be elided in redundant cases,+;;; and in cases where they are needed, performing the checks early, at+;;; procedure entry, can "lift" a check out of a loop. +;;;+;;; Finally, I have only checked the properties that can portably be checked+;;; with R5RS Scheme -- and this is not complete. You may wish to alter+;;; the CHECK-ARG parameter checks to perform extra, implementation-specific+;;; checks, such as procedure arity for higher-order values.+;;;+;;; The code has only these non-R4RS dependencies:+;;;   A few calls to an ERROR procedure;+;;;   Uses of the R5RS multiple-value procedure VALUES and the m-v binding+;;;     RECEIVE macro (which isn't R5RS, but is a trivial macro).+;;;   Many calls to a parameter-checking procedure check-arg:+;;;    (define (check-arg pred val caller)+;;;      (let lp ((val val))+;;;        (if (pred val) val (lp (error "Bad argument" val pred caller)))))+;;;   A few uses of the LET-OPTIONAL and :OPTIONAL macros for parsing+;;;     optional arguments.+;;;+;;; Most of these procedures use the NULL-LIST? test to trigger the+;;; base case in the inner loop or recursion. The NULL-LIST? function+;;; is defined to be a careful one -- it raises an error if passed a+;;; non-nil, non-pair value. The spec allows an implementation to use+;;; a less-careful implementation that simply defines NULL-LIST? to+;;; be NOT-PAIR?. This would speed up the inner loops of these procedures+;;; at the expense of having them silently accept dotted lists.++;;; A note on dotted lists:+;;; I, personally, take the view that the only consistent view of lists+;;; in Scheme is the view that *everything* is a list -- values such as+;;; 3 or "foo" or 'bar are simply empty dotted lists. This is due to the+;;; fact that Scheme actually has no true list type. It has a pair type,+;;; and there is an *interpretation* of the trees built using this type+;;; as lists.+;;;+;;; I lobbied to have these list-processing procedures hew to this+;;; view, and accept any value as a list argument. I was overwhelmingly+;;; overruled during the SRFI discussion phase. So I am inserting this+;;; text in the reference lib and the SRFI spec as a sort of "minority+;;; opinion" dissent.+;;;+;;; Many of the procedures in this library can be trivially redefined+;;; to handle dotted lists, just by changing the NULL-LIST? base-case+;;; check to NOT-PAIR?, meaning that any non-pair value is taken to be+;;; an empty list. For most of these procedures, that's all that is+;;; required.+;;;+;;; However, we have to do a little more work for some procedures that+;;; *produce* lists from other lists.  Were we to extend these procedures to+;;; accept dotted lists, we would have to define how they terminate the lists+;;; produced as results when passed a dotted list. I designed a coherent set+;;; of termination rules for these cases; this was posted to the SRFI-1+;;; discussion list. I additionally wrote an earlier version of this library+;;; that implemented that spec. It has been discarded during later phases of+;;; the definition and implementation of this library.+;;;+;;; The argument *against* defining these procedures to work on dotted+;;; lists is that dotted lists are the rare, odd case, and that by +;;; arranging for the procedures to handle them, we lose error checking+;;; in the cases where a dotted list is passed by accident -- e.g., when+;;; the programmer swaps a two arguments to a list-processing function,+;;; one being a scalar and one being a list. For example,+;;;     (member '(1 3 5 7 9) 7)+;;; This would quietly return #f if we extended MEMBER to accept dotted+;;; lists.+;;;+;;; The SRFI discussion record contains more discussion on this topic.+++; JAE - These are the necessary helper constructs+; +; TBD: Should these be moved to another file, and/or+;      encapsulated to only this srfi ??+(define (check-arg pred val caller)+  (let lp ((val val))+    (if (pred val) val (lp (error "Bad argument" val pred caller)))))++; TODO: unfortunately the original version of this was written using an+; explicit-renaming low level macro system, so it is not portable to R5RS.+; See: http://www.scsh.net/mail-archive/scsh-users/1996-04/msg00010.html+;+;;; (LET-OPTIONALS args ((var1 default1) ...) body1 ...)+;;; ;;; The expander is defined in the code above.+;+;(define-syntax let-optionals expand-let-optionals)++;;; (:optional rest-arg default-exp)+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;; This form is for evaluating optional arguments and their defaults+;;; in simple procedures that take a *single* optional argument. It is+;;; a macro so that the default will not be computed unless it is needed.+;;; +;;; REST-ARG is a rest list from a lambda -- e.g., R in+;;;     (lambda (a b . r) ...)+;;; - If REST-ARG has 0 elements, evaluate DEFAULT-EXP and return that.+;;; - If REST-ARG has 1 element, return that element.+;;; - If REST-ARG has >1 element, error.++(define-syntax :optional+  (syntax-rules ()+    ((:optional rest default-exp)+     (let ((maybe-arg rest))+       (cond ((null? maybe-arg) default-exp)+             ((null? (cdr maybe-arg)) (car maybe-arg))+             (else (error "too many optional arguments" maybe-arg)))))))+++;;; Constructors+;;;;;;;;;;;;;;;;++;;; Occasionally useful as a value to be passed to a fold or other+;;; higher-order procedure.+(define (xcons d a) (cons a d))++;;;; Recursively copy every cons.+;(define (tree-copy x)+;  (let recur ((x x))+;    (if (not (pair? x)) x+;	(cons (recur (car x)) (recur (cdr x))))))++;;; Make a list of length LEN.++(define (make-list len . maybe-elt)+  (check-arg (lambda (n) (and (integer? n) (>= n 0))) len make-list)+  (let ((elt (cond ((null? maybe-elt) #f) ; Default value+		   ((null? (cdr maybe-elt)) (car maybe-elt))+		   (else (error "Too many arguments to MAKE-LIST"+				(cons len maybe-elt))))))+    (do ((i len (- i 1))+	 (ans '() (cons elt ans)))+	((<= i 0) ans))))+++;(define (list . ans) ans)	; R4RS+++;;; Make a list of length LEN. Elt i is (PROC i) for 0 <= i < LEN.++(define (list-tabulate len proc)+  (check-arg (lambda (n) (and (integer? n) (>= n 0))) len list-tabulate)+  (check-arg procedure? proc list-tabulate)+  (do ((i (- len 1) (- i 1))+       (ans '() (cons (proc i) ans)))+      ((< i 0) ans)))++;;; (cons* a1 a2 ... an) = (cons a1 (cons a2 (cons ... an)))+;;; (cons* a1) = a1	(cons* a1 a2 ...) = (cons a1 (cons* a2 ...))+;;;+;;; (cons first (unfold not-pair? car cdr rest values))++(define (cons* first . rest)+  (let recur ((x first) (rest rest))+    (if (pair? rest)+	(cons x (recur (car rest) (cdr rest)))+	x)))++;;; (unfold not-pair? car cdr lis values)++(define (list-copy lis)				+  (let recur ((lis lis))			+    (if (pair? lis)				+	(cons (car lis) (recur (cdr lis)))	+	lis)))					++;;; IOTA count [start step]	(start start+step ... start+(count-1)*step)++(define (iota count . maybe-start+step)+  (check-arg integer? count iota)+  (if (< count 0) (error "Negative step count" iota count))++; JAE+;+; Replaced the let-optionals macro below with code that is+; almost equivalent - but the caller must specify either+; both parameters or none at all.+;+;  (let-optionals maybe-start+step ((start 0) (step 1))+  (let ((start 0) (step 1))+    (if (not (null? maybe-start+step))+      (begin+        (set! start (first maybe-start+step))+        (set! step (second maybe-start+step))))+;/JAE+    (check-arg number? start iota)+    (check-arg number? step iota)+    (let loop ((n 0) (r '()))+      (if (= n count)+	  (reverse r)+	  (loop (+ 1 n)+		(cons (+ start (* n step)) r))))))+	  +;;; I thought these were lovely, but the public at large did not share my+;;; enthusiasm...+;;; :IOTA to		(0 ... to-1)+;;; :IOTA from to	(from ... to-1)+;;; :IOTA from to step  (from from+step ...)++;;; IOTA: to		(1 ... to)+;;; IOTA: from to	(from+1 ... to)+;;; IOTA: from to step	(from+step from+2step ...)++;(define (%parse-iota-args arg1 rest-args proc)+;  (let ((check (lambda (n) (check-arg integer? n proc))))+;    (check arg1)+;    (if (pair? rest-args)+;	(let ((arg2 (check (car rest-args)))+;	      (rest (cdr rest-args)))+;	  (if (pair? rest)+;	      (let ((arg3 (check (car rest)))+;		    (rest (cdr rest)))+;		(if (pair? rest) (error "Too many parameters" proc arg1 rest-args)+;		    (values arg1 arg2 arg3)))+;	      (values arg1 arg2 1)))+;	(values 0 arg1 1))))+;+;(define (iota: arg1 . rest-args)+;  (receive (from to step) (%parse-iota-args arg1 rest-args iota:)+;    (let* ((numsteps (floor (/ (- to from) step)))+;	   (last-val (+ from (* step numsteps))))+;      (if (< numsteps 0) (error "Negative step count" iota: from to step))+;      (do ((steps-left numsteps (- steps-left 1))+;	   (val last-val (- val step))+;	   (ans '() (cons val ans)))+;	  ((<= steps-left 0) ans)))))+;+;+;(define (:iota arg1 . rest-args)+;  (receive (from to step) (%parse-iota-args arg1 rest-args :iota)+;    (let* ((numsteps (ceiling (/ (- to from) step)))+;	   (last-val (+ from (* step (- numsteps 1)))))+;      (if (< numsteps 0) (error "Negative step count" :iota from to step))+;      (do ((steps-left numsteps (- steps-left 1))+;	   (val last-val (- val step))+;	   (ans '() (cons val ans)))+;	  ((<= steps-left 0) ans)))))++++(define (circular-list val1 . vals)+  (let ((ans (cons val1 vals)))+    (set-cdr! (last-pair ans) ans)+    ans))++;;; <proper-list> ::= ()			; Empty proper list+;;;		  |   (cons <x> <proper-list>)	; Proper-list pair+;;; Note that this definition rules out circular lists -- and this+;;; function is required to detect this case and return false.++(define (proper-list? x)+  (let lp ((x x) (lag x))+    (if (pair? x)+	(let ((x (cdr x)))+	  (if (pair? x)+	      (let ((x   (cdr x))+		    (lag (cdr lag)))+		(and (not (eq? x lag)) (lp x lag)))+	      (null? x)))+	(null? x))))+++;;; A dotted list is a finite list (possibly of length 0) terminated+;;; by a non-nil value. Any non-cons, non-nil value (e.g., "foo" or 5)+;;; is a dotted list of length 0.+;;;+;;; <dotted-list> ::= <non-nil,non-pair>	; Empty dotted list+;;;               |   (cons <x> <dotted-list>)	; Proper-list pair++(define (dotted-list? x)+  (let lp ((x x) (lag x))+    (if (pair? x)+	(let ((x (cdr x)))+	  (if (pair? x)+	      (let ((x   (cdr x))+		    (lag (cdr lag)))+		(and (not (eq? x lag)) (lp x lag)))+	      (not (null? x))))+	(not (null? x)))))++(define (circular-list? x)+  (let lp ((x x) (lag x))+    (and (pair? x)+	 (let ((x (cdr x)))+	   (and (pair? x)+		(let ((x   (cdr x))+		      (lag (cdr lag)))+		  (or (eq? x lag) (lp x lag))))))))++(define (not-pair? x) (not (pair? x)))	; Inline me.++;;; This is a legal definition which is fast and sloppy:+;;;     (define null-list? not-pair?)+;;; but we'll provide a more careful one:+(define (null-list? l)+  (cond ((pair? l) #f)+	((null? l) #t)+	(else (error "null-list?: argument out of domain" l))))+           ++(define (list= = . lists)+  (or (null? lists) ; special case++      (let lp1 ((list-a (car lists)) (others (cdr lists)))+	(or (null? others)+	    (let ((list-b (car others))+		  (others (cdr others)))+	      (if (eq? list-a list-b)	; EQ? => LIST=+		  (lp1 list-b others)+		  (let lp2 ((list-a list-a) (list-b list-b))+		    (if (null-list? list-a)+			(and (null-list? list-b)+			     (lp1 list-b others))+			(and (not (null-list? list-b))+			     (= (car list-a) (car list-b))+			     (lp2 (cdr list-a) (cdr list-b)))))))))))+			+++;;; R4RS, so commented out.+;(define (length x)			; LENGTH may diverge or+;  (let lp ((x x) (len 0))		; raise an error if X is+;    (if (pair? x)			; a circular list. This version+;        (lp (cdr x) (+ len 1))		; diverges.+;        len)))++(define (length+ x)			; Returns #f if X is circular.+  (let lp ((x x) (lag x) (len 0))+    (if (pair? x)+	(let ((x (cdr x))+	      (len (+ len 1)))+	  (if (pair? x)+	      (let ((x   (cdr x))+		    (lag (cdr lag))+		    (len (+ len 1)))+		(and (not (eq? x lag)) (lp x lag len)))+	      len))+	len)))++(define (zip list1 . more-lists) (apply map list list1 more-lists))+++;;; Selectors+;;;;;;;;;;;;;++;;; R4RS non-primitives:+;(define (caar   x) (car (car x)))+;(define (cadr   x) (car (cdr x)))+;(define (cdar   x) (cdr (car x)))+;(define (cddr   x) (cdr (cdr x)))+;+;(define (caaar  x) (caar (car x)))+;(define (caadr  x) (caar (cdr x)))+;(define (cadar  x) (cadr (car x)))+;(define (caddr  x) (cadr (cdr x)))+;(define (cdaar  x) (cdar (car x)))+;(define (cdadr  x) (cdar (cdr x)))+;(define (cddar  x) (cddr (car x)))+;(define (cdddr  x) (cddr (cdr x)))+;+;(define (caaaar x) (caaar (car x)))+;(define (caaadr x) (caaar (cdr x)))+;(define (caadar x) (caadr (car x)))+;(define (caaddr x) (caadr (cdr x)))+;(define (cadaar x) (cadar (car x)))+;(define (cadadr x) (cadar (cdr x)))+;(define (caddar x) (caddr (car x)))+;(define (cadddr x) (caddr (cdr x)))+;(define (cdaaar x) (cdaar (car x)))+;(define (cdaadr x) (cdaar (cdr x)))+;(define (cdadar x) (cdadr (car x)))+;(define (cdaddr x) (cdadr (cdr x)))+;(define (cddaar x) (cddar (car x)))+;(define (cddadr x) (cddar (cdr x)))+;(define (cdddar x) (cdddr (car x)))+;(define (cddddr x) (cdddr (cdr x)))+++(define first  car)+(define second cadr)+(define third  caddr)+(define fourth cadddr)+(define (fifth   x) (car    (cddddr x)))+(define (sixth   x) (cadr   (cddddr x)))+(define (seventh x) (caddr  (cddddr x)))+(define (eighth  x) (cadddr (cddddr x)))+(define (ninth   x) (car  (cddddr (cddddr x))))+(define (tenth   x) (cadr (cddddr (cddddr x))))++(define (car+cdr pair) (values (car pair) (cdr pair)))++;;; take & drop++(define (take lis k)+  (check-arg integer? k take)+  (let recur ((lis lis) (k k))+    (if (zero? k) '()+	(cons (car lis)+	      (recur (cdr lis) (- k 1))))))++(define (drop lis k)+  (check-arg integer? k drop)+  (let iter ((lis lis) (k k))+    (if (zero? k) lis (iter (cdr lis) (- k 1)))))++(define (take! lis k)+  (check-arg integer? k take!)+  (if (zero? k) '()+      (begin (set-cdr! (drop lis (- k 1)) '())+	     lis)))++;;; TAKE-RIGHT and DROP-RIGHT work by getting two pointers into the list, +;;; off by K, then chasing down the list until the lead pointer falls off+;;; the end.++(define (take-right lis k)+  (check-arg integer? k take-right)+  (let lp ((lag lis)  (lead (drop lis k)))+    (if (pair? lead)+	(lp (cdr lag) (cdr lead))+	lag)))++(define (drop-right lis k)+  (check-arg integer? k drop-right)+  (let recur ((lag lis) (lead (drop lis k)))+    (if (pair? lead)+	(cons (car lag) (recur (cdr lag) (cdr lead)))+	'())))++;;; In this function, LEAD is actually K+1 ahead of LAG. This lets+;;; us stop LAG one step early, in time to smash its cdr to ().+(define (drop-right! lis k)+  (check-arg integer? k drop-right!)+  (let ((lead (drop lis k)))+    (if (pair? lead)++	(let lp ((lag lis)  (lead (cdr lead)))	; Standard case+	  (if (pair? lead)+	      (lp (cdr lag) (cdr lead))+	      (begin (set-cdr! lag '())+		     lis)))++	'())))	; Special case dropping everything -- no cons to side-effect.++;(define (list-ref lis i) (car (drop lis i)))	; R4RS++;;; These use the APL convention, whereby negative indices mean +;;; "from the right." I liked them, but they didn't win over the+;;; SRFI reviewers.+;;; K >= 0: Take and drop  K elts from the front of the list.+;;; K <= 0: Take and drop -K elts from the end   of the list.++;(define (take lis k)+;  (check-arg integer? k take)+;  (if (negative? k)+;      (list-tail lis (+ k (length lis)))+;      (let recur ((lis lis) (k k))+;	(if (zero? k) '()+;	    (cons (car lis)+;		  (recur (cdr lis) (- k 1)))))))+;+;(define (drop lis k)+;  (check-arg integer? k drop)+;  (if (negative? k)+;      (let recur ((lis lis) (nelts (+ k (length lis))))+;	(if (zero? nelts) '()+;	    (cons (car lis)+;		  (recur (cdr lis) (- nelts 1)))))+;      (list-tail lis k)))+;+;+;(define (take! lis k)+;  (check-arg integer? k take!)+;  (cond ((zero? k) '())+;	((positive? k)+;	 (set-cdr! (list-tail lis (- k 1)) '())+;	 lis)+;	(else (list-tail lis (+ k (length lis))))))+;+;(define (drop! lis k)+;  (check-arg integer? k drop!)+;  (if (negative? k)+;      (let ((nelts (+ k (length lis))))+;	(if (zero? nelts) '()+;	    (begin (set-cdr! (list-tail lis (- nelts 1)) '())+;		   lis)))+;      (list-tail lis k)))++(define (split-at x k)+  (check-arg integer? k split-at)+  (let recur ((lis x) (k k))+    (if (zero? k) (values '() lis)+	(receive (prefix suffix) (recur (cdr lis) (- k 1))+	  (values (cons (car lis) prefix) suffix)))))++(define (split-at! x k)+  (check-arg integer? k split-at!)+  (if (zero? k) (values '() x)+      (let* ((prev (drop x (- k 1)))+	     (suffix (cdr prev)))+	(set-cdr! prev '())+	(values x suffix))))+++(define (last lis) (car (last-pair lis)))++(define (last-pair lis)+  (check-arg pair? lis last-pair)+  (let lp ((lis lis))+    (let ((tail (cdr lis)))+      (if (pair? tail) (lp tail) lis))))+++;;; Unzippers -- 1 through 5+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++(define (unzip1 lis) (map car lis))++(define (unzip2 lis)+  (let recur ((lis lis))+    (if (null-list? lis) (values lis lis)	; Use NOT-PAIR? to handle+	(let ((elt (car lis)))			; dotted lists.+	  (receive (a b) (recur (cdr lis))+	    (values (cons (car  elt) a)+		    (cons (cadr elt) b)))))))++(define (unzip3 lis)+  (let recur ((lis lis))+    (if (null-list? lis) (values lis lis lis)+	(let ((elt (car lis)))+	  (receive (a b c) (recur (cdr lis))+	    (values (cons (car   elt) a)+		    (cons (cadr  elt) b)+		    (cons (caddr elt) c)))))))++(define (unzip4 lis)+  (let recur ((lis lis))+    (if (null-list? lis) (values lis lis lis lis)+	(let ((elt (car lis)))+	  (receive (a b c d) (recur (cdr lis))+	    (values (cons (car    elt) a)+		    (cons (cadr   elt) b)+		    (cons (caddr  elt) c)+		    (cons (cadddr elt) d)))))))++(define (unzip5 lis)+  (let recur ((lis lis))+    (if (null-list? lis) (values lis lis lis lis lis)+	(let ((elt (car lis)))+	  (receive (a b c d e) (recur (cdr lis))+	    (values (cons (car     elt) a)+		    (cons (cadr    elt) b)+		    (cons (caddr   elt) c)+		    (cons (cadddr  elt) d)+		    (cons (car (cddddr  elt)) e)))))))+++;;; append! append-reverse append-reverse! concatenate concatenate!+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++(define (append! . lists)+  ;; First, scan through lists looking for a non-empty one.+  (let lp ((lists lists) (prev '()))+    (if (not (pair? lists)) prev+	(let ((first (car lists))+	      (rest (cdr lists)))+	  (if (not (pair? first)) (lp rest first)++	      ;; Now, do the splicing.+	      (let lp2 ((tail-cons (last-pair first))+			(rest rest))+		(if (pair? rest)+		    (let ((next (car rest))+			  (rest (cdr rest)))+		      (set-cdr! tail-cons next)+		      (lp2 (if (pair? next) (last-pair next) tail-cons)+			   rest))+		    first)))))))++;;; APPEND is R4RS.+;(define (append . lists)+;  (if (pair? lists)+;      (let recur ((list1 (car lists)) (lists (cdr lists)))+;        (if (pair? lists)+;            (let ((tail (recur (car lists) (cdr lists))))+;              (fold-right cons tail list1)) ; Append LIST1 & TAIL.+;            list1))+;      '()))++;(define (append-reverse rev-head tail) (fold cons tail rev-head))++;(define (append-reverse! rev-head tail)+;  (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair)+;             tail+;             rev-head))++;;; Hand-inline the FOLD and PAIR-FOLD ops for speed.++(define (append-reverse rev-head tail)+  (let lp ((rev-head rev-head) (tail tail))+    (if (null-list? rev-head) tail+	(lp (cdr rev-head) (cons (car rev-head) tail)))))++(define (append-reverse! rev-head tail)+  (let lp ((rev-head rev-head) (tail tail))+    (if (null-list? rev-head) tail+	(let ((next-rev (cdr rev-head)))+	  (set-cdr! rev-head tail)+	  (lp next-rev rev-head)))))+++(define (concatenate  lists) (reduce-right append  '() lists))+(define (concatenate! lists) (reduce-right append! '() lists))++;;; Fold/map internal utilities+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;; These little internal utilities are used by the general+;;; fold & mapper funs for the n-ary cases . It'd be nice if they got inlined.+;;; One the other hand, the n-ary cases are painfully inefficient as it is.+;;; An aggressive implementation should simply re-write these functions +;;; for raw efficiency; I have written them for as much clarity, portability,+;;; and simplicity as can be achieved.+;;;+;;; I use the dreaded call/cc to do local aborts. A good compiler could+;;; handle this with extreme efficiency. An implementation that provides+;;; a one-shot, non-persistent continuation grabber could help the compiler+;;; out by using that in place of the call/cc's in these routines.+;;;+;;; These functions have funky definitions that are precisely tuned to+;;; the needs of the fold/map procs -- for example, to minimize the number+;;; of times the argument lists need to be examined.++;;; Return (map cdr lists). +;;; However, if any element of LISTS is empty, just abort and return '().+(define (%cdrs lists)+  (call-with-current-continuation+    (lambda (abort)+      (let recur ((lists lists))+	(if (pair? lists)+	    (let ((lis (car lists)))+	      (if (null-list? lis) (abort '())+		  (cons (cdr lis) (recur (cdr lists)))))+	    '())))))++(define (%cars+ lists last-elt)	; (append! (map car lists) (list last-elt))+  (let recur ((lists lists))+    (if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt))))++;;; LISTS is a (not very long) non-empty list of lists.+;;; Return two lists: the cars & the cdrs of the lists.+;;; However, if any of the lists is empty, just abort and return [() ()].++(define (%cars+cdrs lists)+  (call-with-current-continuation+    (lambda (abort)+      (let recur ((lists lists))+        (if (pair? lists)+	    (receive (list other-lists) (car+cdr lists)+	      (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out+		  (receive (a d) (car+cdr list)+		    (receive (cars cdrs) (recur other-lists)+		      (values (cons a cars) (cons d cdrs))))))+	    (values '() '()))))))++;;; Like %CARS+CDRS, but we pass in a final elt tacked onto the end of the+;;; cars list. What a hack.+(define (%cars+cdrs+ lists cars-final)+  (call-with-current-continuation+    (lambda (abort)+      (let recur ((lists lists))+        (if (pair? lists)+	    (receive (list other-lists) (car+cdr lists)+	      (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out+		  (receive (a d) (car+cdr list)+		    (receive (cars cdrs) (recur other-lists)+		      (values (cons a cars) (cons d cdrs))))))+	    (values (list cars-final) '()))))))++;;; Like %CARS+CDRS, but blow up if any list is empty.+(define (%cars+cdrs/no-test lists)+  (let recur ((lists lists))+    (if (pair? lists)+	(receive (list other-lists) (car+cdr lists)+	  (receive (a d) (car+cdr list)+	    (receive (cars cdrs) (recur other-lists)+	      (values (cons a cars) (cons d cdrs)))))+	(values '() '()))))+++;;; count+;;;;;;;;;+(define (count pred list1 . lists)+  (check-arg procedure? pred count)+  (if (pair? lists)++      ;; N-ary case+      (let lp ((list1 list1) (lists lists) (i 0))+	(if (null-list? list1) i+	    (receive (as ds) (%cars+cdrs lists)+	      (if (null? as) i+		  (lp (cdr list1) ds+		      (if (apply pred (car list1) as) (+ i 1) i))))))++      ;; Fast path+      (let lp ((lis list1) (i 0))+	(if (null-list? lis) i+	    (lp (cdr lis) (if (pred (car lis)) (+ i 1) i))))))+++;;; fold/unfold+;;;;;;;;;;;;;;;++(define (unfold-right p f g seed . maybe-tail)+  (check-arg procedure? p unfold-right)+  (check-arg procedure? f unfold-right)+  (check-arg procedure? g unfold-right)+  (let lp ((seed seed) (ans (:optional maybe-tail '())))+    (if (p seed) ans+	(lp (g seed)+	    (cons (f seed) ans)))))+++(define (unfold p f g seed . maybe-tail-gen)+  (check-arg procedure? p unfold)+  (check-arg procedure? f unfold)+  (check-arg procedure? g unfold)+  (if (pair? maybe-tail-gen)++      (let ((tail-gen (car maybe-tail-gen)))+	(if (pair? (cdr maybe-tail-gen))+	    (apply error "Too many arguments" unfold p f g seed maybe-tail-gen)++	    (let recur ((seed seed))+	      (if (p seed) (tail-gen seed)+		  (cons (f seed) (recur (g seed)))))))++      (let recur ((seed seed))+	(if (p seed) '()+	    (cons (f seed) (recur (g seed)))))))+      ++(define (fold kons knil lis1 . lists)+  (check-arg procedure? kons fold)+  (if (pair? lists)+      (let lp ((lists (cons lis1 lists)) (ans knil))	; N-ary case+	(receive (cars+ans cdrs) (%cars+cdrs+ lists ans)+	  (if (null? cars+ans) ans ; Done.+	      (lp cdrs (apply kons cars+ans)))))+	    +      (let lp ((lis lis1) (ans knil))			; Fast path+	(if (null-list? lis) ans+	    (lp (cdr lis) (kons (car lis) ans))))))+++(define (fold-right kons knil lis1 . lists)+  (check-arg procedure? kons fold-right)+  (if (pair? lists)+      (let recur ((lists (cons lis1 lists)))		; N-ary case+	(let ((cdrs (%cdrs lists)))+	  (if (null? cdrs) knil+	      (apply kons (%cars+ lists (recur cdrs))))))++      (let recur ((lis lis1))				; Fast path+	(if (null-list? lis) knil+	    (let ((head (car lis)))+	      (kons head (recur (cdr lis))))))))+++(define (pair-fold-right f zero lis1 . lists)+  (check-arg procedure? f pair-fold-right)+  (if (pair? lists)+      (let recur ((lists (cons lis1 lists)))		; N-ary case+	(let ((cdrs (%cdrs lists)))+	  (if (null? cdrs) zero+	      (apply f (append! lists (list (recur cdrs)))))))++      (let recur ((lis lis1))				; Fast path+	(if (null-list? lis) zero (f lis (recur (cdr lis)))))))++(define (pair-fold f zero lis1 . lists)+  (check-arg procedure? f pair-fold)+  (if (pair? lists)+      (let lp ((lists (cons lis1 lists)) (ans zero))	; N-ary case+	(let ((tails (%cdrs lists)))+	  (if (null? tails) ans+	      (lp tails (apply f (append! lists (list ans)))))))++      (let lp ((lis lis1) (ans zero))+	(if (null-list? lis) ans+	    (let ((tail (cdr lis)))		; Grab the cdr now,+	      (lp tail (f lis ans)))))))	; in case F SET-CDR!s LIS.+      ++;;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case.+;;; These cannot meaningfully be n-ary.++(define (reduce f ridentity lis)+  (check-arg procedure? f reduce)+  (if (null-list? lis) ridentity+      (fold f (car lis) (cdr lis))))++(define (reduce-right f ridentity lis)+  (check-arg procedure? f reduce-right)+  (if (null-list? lis) ridentity+      (let recur ((head (car lis)) (lis (cdr lis)))+	(if (pair? lis)+	    (f head (recur (car lis) (cdr lis)))+	    head))))++++;;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++(define (append-map f lis1 . lists)+  (really-append-map append-map  append  f lis1 lists))+(define (append-map! f lis1 . lists) +  (really-append-map append-map! append! f lis1 lists))++(define (really-append-map who appender f lis1 lists)+  (check-arg procedure? f who)+  (if (pair? lists)+      (receive (cars cdrs) (%cars+cdrs (cons lis1 lists))+	(if (null? cars) '()+	    (let recur ((cars cars) (cdrs cdrs))+	      (let ((vals (apply f cars)))+		(receive (cars2 cdrs2) (%cars+cdrs cdrs)+		  (if (null? cars2) vals+		      (appender vals (recur cars2 cdrs2))))))))++      ;; Fast path+      (if (null-list? lis1) '()+	  (let recur ((elt (car lis1)) (rest (cdr lis1)))+	    (let ((vals (f elt)))+	      (if (null-list? rest) vals+		  (appender vals (recur (car rest) (cdr rest)))))))))+++(define (pair-for-each proc lis1 . lists)+  (check-arg procedure? proc pair-for-each)+  (if (pair? lists)++      (let lp ((lists (cons lis1 lists)))+	(let ((tails (%cdrs lists)))+	  (if (pair? tails)+	      (begin (apply proc lists)+		     (lp tails)))))++      ;; Fast path.+      (let lp ((lis lis1))+	(if (not (null-list? lis))+	    (let ((tail (cdr lis)))	; Grab the cdr now,+	      (proc lis)		; in case PROC SET-CDR!s LIS.+	      (lp tail))))))++;;; We stop when LIS1 runs out, not when any list runs out.+(define (map! f lis1 . lists)+  (check-arg procedure? f map!)+  (if (pair? lists)+      (let lp ((lis1 lis1) (lists lists))+	(if (not (null-list? lis1))+	    (receive (heads tails) (%cars+cdrs/no-test lists)+	      (set-car! lis1 (apply f (car lis1) heads))+	      (lp (cdr lis1) tails))))++      ;; Fast path.+      (pair-for-each (lambda (pair) (set-car! pair (f (car pair)))) lis1))+  lis1)+++;;; Map F across L, and save up all the non-false results.+(define (filter-map f lis1 . lists)+  (check-arg procedure? f filter-map)+  (if (pair? lists)+      (let recur ((lists (cons lis1 lists)))+	(receive (cars cdrs) (%cars+cdrs lists)+	  (if (pair? cars)+	      (cond ((apply f cars) => (lambda (x) (cons x (recur cdrs))))+		    (else (recur cdrs))) ; Tail call in this arm.+	      '())))+	    +      ;; Fast path.+      (let recur ((lis lis1))+	(if (null-list? lis) lis+	    (let ((tail (recur (cdr lis))))+	      (cond ((f (car lis)) => (lambda (x) (cons x tail)))+		    (else tail)))))))+++;;; Map F across lists, guaranteeing to go left-to-right.+;;; NOTE: Some implementations of R5RS MAP are compliant with this spec;+;;; in which case this procedure may simply be defined as a synonym for MAP.++(define (map-in-order f lis1 . lists)+  (check-arg procedure? f map-in-order)+  (if (pair? lists)+      (let recur ((lists (cons lis1 lists)))+	(receive (cars cdrs) (%cars+cdrs lists)+	  (if (pair? cars)+	      (let ((x (apply f cars)))		; Do head first,+		(cons x (recur cdrs)))		; then tail.+	      '())))+	    +      ;; Fast path.+      (let recur ((lis lis1))+	(if (null-list? lis) lis+	    (let ((tail (cdr lis))+		  (x (f (car lis))))		; Do head first,+	      (cons x (recur tail)))))))	; then tail.+++;;; We extend MAP to handle arguments of unequal length.+(define map map-in-order)	+++;;; filter, remove, partition+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;; FILTER, REMOVE, PARTITION and their destructive counterparts do not+;;; disorder the elements of their argument.++;; This FILTER shares the longest tail of L that has no deleted elements.+;; If Scheme had multi-continuation calls, they could be made more efficient.++(define (filter pred lis)			; Sleazing with EQ? makes this+  (check-arg procedure? pred filter)		; one faster.+  (let recur ((lis lis))		+    (if (null-list? lis) lis			; Use NOT-PAIR? to handle dotted lists.+	(let ((head (car lis))+	      (tail (cdr lis)))+	  (if (pred head)+	      (let ((new-tail (recur tail)))	; Replicate the RECUR call so+		(if (eq? tail new-tail) lis+		    (cons head new-tail)))+	      (recur tail))))))			; this one can be a tail call.+++;;; Another version that shares longest tail.+;(define (filter pred lis)+;  (receive (ans no-del?)+;      ;; (recur l) returns L with (pred x) values filtered.+;      ;; It also returns a flag NO-DEL? if the returned value+;      ;; is EQ? to L, i.e. if it didn't have to delete anything.+;      (let recur ((l l))+;	(if (null-list? l) (values l #t)+;	    (let ((x  (car l))+;		  (tl (cdr l)))+;	      (if (pred x)+;		  (receive (ans no-del?) (recur tl)+;		    (if no-del?+;			(values l #t)+;			(values (cons x ans) #f)))+;		  (receive (ans no-del?) (recur tl) ; Delete X.+;		    (values ans #f))))))+;    ans))++++;(define (filter! pred lis)			; Things are much simpler+;  (let recur ((lis lis))			; if you are willing to+;    (if (pair? lis)				; push N stack frames & do N+;        (cond ((pred (car lis))		; SET-CDR! writes, where N is+;               (set-cdr! lis (recur (cdr lis))); the length of the answer.+;               lis)				+;              (else (recur (cdr lis))))+;        lis)))+++;;; This implementation of FILTER!+;;; - doesn't cons, and uses no stack;+;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are +;;;   usually expensive on modern machines, and can be extremely expensive on +;;;   modern Schemes (e.g., ones that have generational GC's).+;;; It just zips down contiguous runs of in and out elts in LIS doing the +;;; minimal number of SET-CDR!s to splice the tail of one run of ins to the +;;; beginning of the next.++(define (filter! pred lis)+  (check-arg procedure? pred filter!)+  (let lp ((ans lis))+    (cond ((null-list? ans)       ans)			; Scan looking for+	  ((not (pred (car ans))) (lp (cdr ans)))	; first cons of result.++	  ;; ANS is the eventual answer.+	  ;; SCAN-IN: (CDR PREV) = LIS and (CAR PREV) satisfies PRED.+	  ;;          Scan over a contiguous segment of the list that+	  ;;          satisfies PRED.+	  ;; SCAN-OUT: (CAR PREV) satisfies PRED. Scan over a contiguous+	  ;;           segment of the list that *doesn't* satisfy PRED.+	  ;;           When the segment ends, patch in a link from PREV+	  ;;           to the start of the next good segment, and jump to+	  ;;           SCAN-IN.+	  (else (letrec ((scan-in (lambda (prev lis)+				    (if (pair? lis)+					(if (pred (car lis))+					    (scan-in lis (cdr lis))+					    (scan-out prev (cdr lis))))))+			 (scan-out (lambda (prev lis)+				     (let lp ((lis lis))+				       (if (pair? lis)+					   (if (pred (car lis))+					       (begin (set-cdr! prev lis)+						      (scan-in lis (cdr lis)))+					       (lp (cdr lis)))+					   (set-cdr! prev lis))))))+		  (scan-in ans (cdr ans))+		  ans)))))++++;;; Answers share common tail with LIS where possible; +;;; the technique is slightly subtle.++(define (partition pred lis)+  (check-arg procedure? pred partition)+  (let recur ((lis lis))+    (if (null-list? lis) (values lis lis)	; Use NOT-PAIR? to handle dotted lists.+	(let ((elt (car lis))+	      (tail (cdr lis)))+	  (receive (in out) (recur tail)+	    (if (pred elt)+		(values (if (pair? out) (cons elt in) lis) out)+		(values in (if (pair? in) (cons elt out) lis))))))))++++;(define (partition! pred lis)			; Things are much simpler+;  (let recur ((lis lis))			; if you are willing to+;    (if (null-list? lis) (values lis lis)	; push N stack frames & do N+;        (let ((elt (car lis)))			; SET-CDR! writes, where N is+;          (receive (in out) (recur (cdr lis))	; the length of LIS.+;            (cond ((pred elt)+;                   (set-cdr! lis in)+;                   (values lis out))+;                  (else (set-cdr! lis out)+;                        (values in lis))))))))+++;;; This implementation of PARTITION!+;;; - doesn't cons, and uses no stack;+;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are+;;;   usually expensive on modern machines, and can be extremely expensive on +;;;   modern Schemes (e.g., ones that have generational GC's).+;;; It just zips down contiguous runs of in and out elts in LIS doing the+;;; minimal number of SET-CDR!s to splice these runs together into the result +;;; lists.++(define (partition! pred lis)+  (check-arg procedure? pred partition!)+  (if (null-list? lis) (values lis lis)++      ;; This pair of loops zips down contiguous in & out runs of the+      ;; list, splicing the runs together. The invariants are+      ;;   SCAN-IN:  (cdr in-prev)  = LIS.+      ;;   SCAN-OUT: (cdr out-prev) = LIS.+      (letrec ((scan-in (lambda (in-prev out-prev lis)+			  (let lp ((in-prev in-prev) (lis lis))+			    (if (pair? lis)+				(if (pred (car lis))+				    (lp lis (cdr lis))+				    (begin (set-cdr! out-prev lis)+					   (scan-out in-prev lis (cdr lis))))+				(set-cdr! out-prev lis))))) ; Done.++	       (scan-out (lambda (in-prev out-prev lis)+			   (let lp ((out-prev out-prev) (lis lis))+			     (if (pair? lis)+				 (if (pred (car lis))+				     (begin (set-cdr! in-prev lis)+					    (scan-in lis out-prev (cdr lis)))+				     (lp lis (cdr lis)))+				 (set-cdr! in-prev lis)))))) ; Done.++	;; Crank up the scan&splice loops.+	(if (pred (car lis))+	    ;; LIS begins in-list. Search for out-list's first pair.+	    (let lp ((prev-l lis) (l (cdr lis)))+	      (cond ((not (pair? l)) (values lis l))+		    ((pred (car l)) (lp l (cdr l)))+		    (else (scan-out prev-l l (cdr l))+			  (values lis l))))	; Done.++	    ;; LIS begins out-list. Search for in-list's first pair.+	    (let lp ((prev-l lis) (l (cdr lis)))+	      (cond ((not (pair? l)) (values l lis))+		    ((pred (car l))+		     (scan-in l prev-l (cdr l))+		     (values l lis))		; Done.+		    (else (lp l (cdr l)))))))))+++;;; Inline us, please.+(define (remove  pred l) (filter  (lambda (x) (not (pred x))) l))+(define (remove! pred l) (filter! (lambda (x) (not (pred x))) l))++++;;; Here's the taxonomy for the DELETE/ASSOC/MEMBER functions.+;;; (I don't actually think these are the world's most important+;;; functions -- the procedural FILTER/REMOVE/FIND/FIND-TAIL variants+;;; are far more general.)+;;;+;;; Function			Action+;;; ---------------------------------------------------------------------------+;;; remove pred lis		Delete by general predicate+;;; delete x lis [=]		Delete by element comparison+;;;					     +;;; find pred lis		Search by general predicate+;;; find-tail pred lis		Search by general predicate+;;; member x lis [=]		Search by element comparison+;;;+;;; assoc key lis [=]		Search alist by key comparison+;;; alist-delete key alist [=]	Alist-delete by key comparison++(define (delete x lis . maybe-=) +  (let ((= (:optional maybe-= equal?)))+    (filter (lambda (y) (not (= x y))) lis)))++(define (delete! x lis . maybe-=)+  (let ((= (:optional maybe-= equal?)))+    (filter! (lambda (y) (not (= x y))) lis)))++;;; Extended from R4RS to take an optional comparison argument.+(define (member x lis . maybe-=)+  (let ((= (:optional maybe-= equal?)))+    (find-tail (lambda (y) (= x y)) lis)))++;;; R4RS, hence we don't bother to define.+;;; The MEMBER and then FIND-TAIL call should definitely+;;; be inlined for MEMQ & MEMV.+;(define (memq    x lis) (member x lis eq?))+;(define (memv    x lis) (member x lis eqv?))+++;;; right-duplicate deletion+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;; delete-duplicates delete-duplicates!+;;;+;;; Beware -- these are N^2 algorithms. To efficiently remove duplicates+;;; in long lists, sort the list to bring duplicates together, then use a +;;; linear-time algorithm to kill the dups. Or use an algorithm based on+;;; element-marking. The former gives you O(n lg n), the latter is linear.++(define (delete-duplicates lis . maybe-=)+  (let ((elt= (:optional maybe-= equal?)))+    (check-arg procedure? elt= delete-duplicates)+    (let recur ((lis lis))+      (if (null-list? lis) lis+	  (let* ((x (car lis))+		 (tail (cdr lis))+		 (new-tail (recur (delete x tail elt=))))+	    (if (eq? tail new-tail) lis (cons x new-tail)))))))++(define (delete-duplicates! lis maybe-=)+  (let ((elt= (:optional maybe-= equal?)))+    (check-arg procedure? elt= delete-duplicates!)+    (let recur ((lis lis))+      (if (null-list? lis) lis+	  (let* ((x (car lis))+		 (tail (cdr lis))+		 (new-tail (recur (delete! x tail elt=))))+	    (if (eq? tail new-tail) lis (cons x new-tail)))))))+++;;; alist stuff+;;;;;;;;;;;;;;;++;;; Extended from R4RS to take an optional comparison argument.+(define (assoc x lis . maybe-=)+  (let ((= (:optional maybe-= equal?)))+    (find (lambda (entry) (= x (car entry))) lis)))++(define (alist-cons key datum alist) (cons (cons key datum) alist))++(define (alist-copy alist)+  (map (lambda (elt) (cons (car elt) (cdr elt)))+       alist))++(define (alist-delete key alist . maybe-=)+  (let ((= (:optional maybe-= equal?)))+    (filter (lambda (elt) (not (= key (car elt)))) alist)))++(define (alist-delete! key alist . maybe-=)+  (let ((= (:optional maybe-= equal?)))+    (filter! (lambda (elt) (not (= key (car elt)))) alist)))+++;;; find find-tail take-while drop-while span break any every list-index+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++(define (find pred list)+  (cond ((find-tail pred list) => car)+	(else #f)))++(define (find-tail pred list)+  (check-arg procedure? pred find-tail)+  (let lp ((list list))+    (and (not (null-list? list))+	 (if (pred (car list)) list+	     (lp (cdr list))))))++(define (take-while pred lis)+  (check-arg procedure? pred take-while)+  (let recur ((lis lis))+    (if (null-list? lis) '()+	(let ((x (car lis)))+	  (if (pred x)+	      (cons x (recur (cdr lis)))+	      '())))))++(define (drop-while pred lis)+  (check-arg procedure? pred drop-while)+  (let lp ((lis lis))+    (if (null-list? lis) '()+	(if (pred (car lis))+	    (lp (cdr lis))+	    lis))))++(define (take-while! pred lis)+  (check-arg procedure? pred take-while!)+  (if (or (null-list? lis) (not (pred (car lis)))) '()+      (begin (let lp ((prev lis) (rest (cdr lis)))+	       (if (pair? rest)+		   (let ((x (car rest)))+		     (if (pred x) (lp rest (cdr rest))+			 (set-cdr! prev '())))))+	     lis)))++(define (span pred lis)+  (check-arg procedure? pred span)+  (let recur ((lis lis))+    (if (null-list? lis) (values '() '())+	(let ((x (car lis)))+	  (if (pred x)+	      (receive (prefix suffix) (recur (cdr lis))+		(values (cons x prefix) suffix))+	      (values '() lis))))))++(define (span! pred lis)+  (check-arg procedure? pred span!)+  (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis)+      (let ((suffix (let lp ((prev lis) (rest (cdr lis)))+		      (if (null-list? rest) rest+			  (let ((x (car rest)))+			    (if (pred x) (lp rest (cdr rest))+				(begin (set-cdr! prev '())+				       rest)))))))+	(values lis suffix))))+  ++(define (break  pred lis) (span  (lambda (x) (not (pred x))) lis))+(define (break! pred lis) (span! (lambda (x) (not (pred x))) lis))++(define (any pred lis1 . lists)+  (check-arg procedure? pred any)+  (if (pair? lists)++      ;; N-ary case+      (receive (heads tails) (%cars+cdrs (cons lis1 lists))+	(and (pair? heads)+	     (let lp ((heads heads) (tails tails))+	       (receive (next-heads next-tails) (%cars+cdrs tails)+		 (if (pair? next-heads)+		     (or (apply pred heads) (lp next-heads next-tails))+		     (apply pred heads)))))) ; Last PRED app is tail call.++      ;; Fast path+      (and (not (null-list? lis1))+	   (let lp ((head (car lis1)) (tail (cdr lis1)))+	     (if (null-list? tail)+		 (pred head)		; Last PRED app is tail call.+		 (or (pred head) (lp (car tail) (cdr tail))))))))+++;(define (every pred list)              ; Simple definition.+;  (let lp ((list list))                ; Doesn't return the last PRED value.+;    (or (not (pair? list))+;        (and (pred (car list))+;             (lp (cdr list))))))++(define (every pred lis1 . lists)+  (check-arg procedure? pred every)+  (if (pair? lists)++      ;; N-ary case+      (receive (heads tails) (%cars+cdrs (cons lis1 lists))+	(or (not (pair? heads))+	    (let lp ((heads heads) (tails tails))+	      (receive (next-heads next-tails) (%cars+cdrs tails)+		(if (pair? next-heads)+		    (and (apply pred heads) (lp next-heads next-tails))+		    (apply pred heads)))))) ; Last PRED app is tail call.++      ;; Fast path+      (or (null-list? lis1)+	  (let lp ((head (car lis1))  (tail (cdr lis1)))+	    (if (null-list? tail)+		(pred head)	; Last PRED app is tail call.+		(and (pred head) (lp (car tail) (cdr tail))))))))++(define (list-index pred lis1 . lists)+  (check-arg procedure? pred list-index)+  (if (pair? lists)++      ;; N-ary case+      (let lp ((lists (cons lis1 lists)) (n 0))+	(receive (heads tails) (%cars+cdrs lists)+	  (and (pair? heads)+	       (if (apply pred heads) n+		   (lp tails (+ n 1))))))++      ;; Fast path+      (let lp ((lis lis1) (n 0))+	(and (not (null-list? lis))+	     (if (pred (car lis)) n (lp (cdr lis) (+ n 1)))))))++;;; Reverse+;;;;;;;;;;;++;R4RS, so not defined here.+;(define (reverse lis) (fold cons '() lis))+				      +;(define (reverse! lis)+;  (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() lis))++(define (reverse! lis)+  (let lp ((lis lis) (ans '()))+    (if (null-list? lis) ans+        (let ((tail (cdr lis)))+          (set-cdr! lis ans)+          (lp tail lis)))))++;;; Lists-as-sets+;;;;;;;;;;;;;;;;;++;;; This is carefully tuned code; do not modify casually.+;;; - It is careful to share storage when possible;+;;; - Side-effecting code tries not to perform redundant writes.+;;; - It tries to avoid linear-time scans in special cases where constant-time+;;;   computations can be performed.+;;; - It relies on similar properties from the other list-lib procs it calls.+;;;   For example, it uses the fact that the implementations of MEMBER and+;;;   FILTER in this source code share longest common tails between args+;;;   and results to get structure sharing in the lset procedures.++(define (%lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1))++(define (lset<= = . lists)+  (check-arg procedure? = lset<=)+  (or (not (pair? lists)) ; 0-ary case+      (let lp ((s1 (car lists)) (rest (cdr lists)))+	(or (not (pair? rest))+	    (let ((s2 (car rest))  (rest (cdr rest)))+	      (and (or (eq? s2 s1)	; Fast path+		       (%lset2<= = s1 s2)) ; Real test+		   (lp s2 rest)))))))++(define (lset= = . lists)+  (check-arg procedure? = lset=)+  (or (not (pair? lists)) ; 0-ary case+      (let lp ((s1 (car lists)) (rest (cdr lists)))+	(or (not (pair? rest))+	    (let ((s2   (car rest))+		  (rest (cdr rest)))+	      (and (or (eq? s1 s2)	; Fast path+		       (and (%lset2<= = s1 s2) (%lset2<= = s2 s1))) ; Real test+		   (lp s2 rest)))))))+++(define (lset-adjoin = lis . elts)+  (check-arg procedure? = lset-adjoin)+  (fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans)))+	lis elts))+++(define (lset-union = . lists)+  (check-arg procedure? = lset-union)+  (reduce (lambda (lis ans)		; Compute ANS + LIS.+	    (cond ((null? lis) ans)	; Don't copy any lists+		  ((null? ans) lis) 	; if we don't have to.+		  ((eq? lis ans) ans)+		  (else+		   (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans)+					       ans+					       (cons elt ans)))+			 ans lis))))+	  '() lists))++(define (lset-union! = . lists)+  (check-arg procedure? = lset-union!)+  (reduce (lambda (lis ans)		; Splice new elts of LIS onto the front of ANS.+	    (cond ((null? lis) ans)	; Don't copy any lists+		  ((null? ans) lis) 	; if we don't have to.+		  ((eq? lis ans) ans)+		  (else+		   (pair-fold (lambda (pair ans)+				(let ((elt (car pair)))+				  (if (any (lambda (x) (= x elt)) ans)+				      ans+				      (begin (set-cdr! pair ans) pair))))+			      ans lis))))+	  '() lists))+++(define (lset-intersection = lis1 . lists)+  (check-arg procedure? = lset-intersection)+  (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.+    (cond ((any null-list? lists) '())		; Short cut+	  ((null? lists)          lis1)		; Short cut+	  (else (filter (lambda (x)+			  (every (lambda (lis) (member x lis =)) lists))+			lis1)))))++(define (lset-intersection! = lis1 . lists)+  (check-arg procedure? = lset-intersection!)+  (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.+    (cond ((any null-list? lists) '())		; Short cut+	  ((null? lists)          lis1)		; Short cut+	  (else (filter! (lambda (x)+			   (every (lambda (lis) (member x lis =)) lists))+			 lis1)))))+++(define (lset-difference = lis1 . lists)+  (check-arg procedure? = lset-difference)+  (let ((lists (filter pair? lists)))	; Throw out empty lists.+    (cond ((null? lists)     lis1)	; Short cut+	  ((memq lis1 lists) '())	; Short cut+	  (else (filter (lambda (x)+			  (every (lambda (lis) (not (member x lis =)))+				 lists))+			lis1)))))++(define (lset-difference! = lis1 . lists)+  (check-arg procedure? = lset-difference!)+  (let ((lists (filter pair? lists)))	; Throw out empty lists.+    (cond ((null? lists)     lis1)	; Short cut+	  ((memq lis1 lists) '())	; Short cut+	  (else (filter! (lambda (x)+			   (every (lambda (lis) (not (member x lis =)))+				  lists))+			 lis1)))))+++(define (lset-xor = . lists)+  (check-arg procedure? = lset-xor)+  (reduce (lambda (b a)			; Compute A xor B:+	    ;; Note that this code relies on the constant-time+	    ;; short-cuts provided by LSET-DIFF+INTERSECTION,+	    ;; LSET-DIFFERENCE & APPEND to provide constant-time short+	    ;; cuts for the cases A = (), B = (), and A eq? B. It takes+	    ;; a careful case analysis to see it, but it's carefully+	    ;; built in.++	    ;; Compute a-b and a^b, then compute b-(a^b) and+	    ;; cons it onto the front of a-b.+	    (receive (a-b a-int-b)   (lset-diff+intersection = a b)+	      (cond ((null? a-b)     (lset-difference = b a))+		    ((null? a-int-b) (append b a))+		    (else (fold (lambda (xb ans)+				  (if (member xb a-int-b =) ans (cons xb ans)))+				a-b+				b)))))+	  '() lists))+++(define (lset-xor! = . lists)+  (check-arg procedure? = lset-xor!)+  (reduce (lambda (b a)			; Compute A xor B:+	    ;; Note that this code relies on the constant-time+	    ;; short-cuts provided by LSET-DIFF+INTERSECTION,+	    ;; LSET-DIFFERENCE & APPEND to provide constant-time short+	    ;; cuts for the cases A = (), B = (), and A eq? B. It takes+	    ;; a careful case analysis to see it, but it's carefully+	    ;; built in.++	    ;; Compute a-b and a^b, then compute b-(a^b) and+	    ;; cons it onto the front of a-b.+	    (receive (a-b a-int-b)   (lset-diff+intersection! = a b)+	      (cond ((null? a-b)     (lset-difference! = b a))+		    ((null? a-int-b) (append! b a))+		    (else (pair-fold (lambda (b-pair ans)+				       (if (member (car b-pair) a-int-b =) ans+					   (begin (set-cdr! b-pair ans) b-pair)))+				     a-b+				     b)))))+	  '() lists))+++(define (lset-diff+intersection = lis1 . lists)+  (check-arg procedure? = lset-diff+intersection)+  (cond ((every null-list? lists) (values lis1 '()))	; Short cut+	((memq lis1 lists)        (values '() lis1))	; Short cut+	(else (partition (lambda (elt)+			   (not (any (lambda (lis) (member elt lis =))+				     lists)))+			 lis1))))++(define (lset-diff+intersection! = lis1 . lists)+  (check-arg procedure? = lset-diff+intersection!)+  (cond ((every null-list? lists) (values lis1 '()))	; Short cut+	((memq lis1 lists)        (values '() lis1))	; Short cut+	(else (partition! (lambda (elt)+			    (not (any (lambda (lis) (member elt lis =))+				      lists)))+			  lis1))))
+ lib/srfi/srfi-2.scm view
@@ -0,0 +1,35 @@+;;;+;;; husk-scheme+;;; http://justinethier.github.com/husk-scheme+;;;+;;; Implementation of SRFI-2: and-let*+;;;+(define-syntax and-let*+  (syntax-rules ()+    ; Special case+    ((and-let* ())+     #t)+    ; No CLAWS, just body+    ((and-let* () body ...)+     (begin body ...))+    ; Special cases of below - CLAWS with no body+    ((and-let* ((var expr)) )+     (let ((var expr))+       (and var)))+    ((and-let* ((expr)))+     (let ((tmp expr))+       (and tmp )))+    ((and-let* (expr))+     (let ((tmp expr))+       (and tmp )))+    ; General case - CLAWS and body+    ((and-let* ((var expr) . rest) . body)+     (let ((var expr))+       (and var (and-let* rest . body))))+    ((and-let* ((expr) . rest) . body)+     (let ((tmp expr))+       (and tmp (and-let* rest . body))))+    ((and-let* (expr . rest) . body)+     (let ((tmp expr))+       (and tmp (and-let* rest . body))))))+
+ lib/srfi/srfi-55.scm view
@@ -0,0 +1,48 @@+;;;+;;; husk-scheme+;;; http://justinethier.github.com/husk-scheme+;;;+;;; Reference implementation for SRFI-55+;;; from http://srfi.schemers.org/srfi-55/srfi-55.html+;;;+;;; Requirements: SRFI-23 (error reporting)+;;;++; +; Example of registering extensions:+;    (register-extension '(srfi 1) "srfi/srfi-1.scm")+; Example of loading an extension:+;   (require-extension (srfi 1))+;   (require-extension (srfi 1 3 4))+;++(define *__env__* (current-environment))+(define available-extensions '())++(define (register-extension id action . compare)+  (set! available-extensions+    (cons (list (if (pair? compare) (car compare) equal?)+		id +		action)+	  available-extensions)) )++(define (find-extension id)+  (define (lookup exts)+    (if (null? exts)+	  (write (list "extension not found - please contact your vendor " id))+	  (let ((ext (car exts)))+	    (if ((car ext) (cadr ext) id)+	        (caddr ext) ; Return a string instead of calling a function ((caddr ext))+	        (lookup (cdr exts)) ) ) ) )+  (lookup available-extensions) )++(define-syntax require-extension +  (syntax-rules (srfi)+    ((_ "internal" (srfi id ...))+     (begin +       (load (find-extension '(srfi id)) *__env__*) ...))+    ((_ "internal" id)+     (load (find-extension 'id) *__env__*))+    ((_ clause ...)+     (begin (require-extension "internal" clause) ...)) ) )+
+ lib/stdlib.scm view
@@ -0,0 +1,502 @@+;;;+;;; husk-scheme+;;; http://justinethier.github.com/husk-scheme+;;;+;;; Written by Justin Ethier+;;;+;;; Standard library of scheme functions+;;;++(define call/cc call-with-current-continuation)++(define (caar pair) (car (car pair)))+(define (cadr pair) (car (cdr pair)))+(define (cdar pair) (cdr (car pair)))+(define (cddr pair) (cdr (cdr pair)))+(define (caaar pair) (car (car (car pair))))+(define (caadr pair) (car (car (cdr pair))))+(define (cadar pair) (car (cdr (car pair))))+(define (caddr pair) (car (cdr (cdr pair))))+(define (cdaar pair) (cdr (car (car pair))))+(define (cdadr pair) (cdr (car (cdr pair))))+(define (cddar pair) (cdr (cdr (car pair))))+(define (cdddr pair) (cdr (cdr (cdr pair))))+(define (caaaar pair) (car (car (car (car pair)))))+(define (caaadr pair) (car (car (car (cdr pair)))))+(define (caadar pair) (car (car (cdr (car pair)))))+(define (caaddr pair) (car (car (cdr (cdr pair)))))+(define (cadaar pair) (car (cdr (car (car pair)))))+(define (cadadr pair) (car (cdr (car (cdr pair)))))+(define (caddar pair) (car (cdr (cdr (car pair)))))+(define (cadddr pair) (car (cdr (cdr (cdr pair)))))+(define (cdaaar pair) (cdr (car (car (car pair)))))+(define (cdaadr pair) (cdr (car (car (cdr pair)))))+(define (cdadar pair) (cdr (car (cdr (car pair)))))+(define (cdaddr pair) (cdr (car (cdr (cdr pair)))))+(define (cddaar pair) (cdr (cdr (car (car pair)))))+(define (cddadr pair) (cdr (cdr (car (cdr pair)))))+(define (cdddar pair) (cdr (cdr (cdr (car pair)))))+(define (cddddr pair) (cdr (cdr (cdr (cdr pair)))))+++(define (not x)      (if x #f #t))++(define (list . objs)  objs)+(define (id obj)       obj)++; TODO: this is not flipping args. not part of R5RS, but +;       as it is now, what is the point?+(define (flip func)    (lambda (arg1 arg2) (func arg1 arg2)))++(define (curry func arg1)  (lambda (arg) (apply func (cons arg1 (list arg)))))+(define (compose f g)      (lambda (arg) (f (apply g arg))))++(define (foldr func end lst)+  (if (null? lst)+	  end+	  (func (car lst) (foldr func end (cdr lst)))))++(define (foldl func accum lst)+  (if (null? lst)+	  accum+	  (foldl func (func (car lst) accum) (cdr lst))))++(define (sum . lst)     (foldl + 0 lst))+(define (product . lst) (foldl * 1 lst))++; Forms from R5RS for and/or+(define-syntax and+  (syntax-rules ()+    ((and) #t)+    ((and test) test)+    ((and test1 test2 ...)+     (if test1 (and test2 ...) #f))))++(define-syntax or+  (syntax-rules ()+    ((or) #f)+    ((or test) test)+    ((or test1 test2 ...)+     (let ((x test1))+       (if x x (or test2 ...))))))++(define (abs num)+  (if (negative? num)+      (* num -1)+      num))++(define (max first . rest) (foldl (lambda (old new) (if (> old new) old new)) first rest))+(define (min first . rest) (foldl (lambda (old new) (if (< old new) old new)) first rest))++(define zero?        (curry = 0))+(define positive?    (curry < 0))+(define negative?    (curry > 0))+(define (odd? num)   (= (modulo num 2) 1))+(define (even? num)  (= (modulo num 2) 0))++(define (length lst)    (foldl (lambda (x y) (+ y 1)) 0 lst))+(define (reverse lst)   (foldl (flip cons) '() lst))++(define-syntax begin+  (syntax-rules ()+    ((begin exp ...)+      ((lambda () exp ...)))))++; cond+; Form from R5RS:+(define-syntax cond+  (syntax-rules (else =>)+    ((cond (else result1 result2 ...))+;+; TODO: see pitfall 3.2+;+; This is a modification from R5RS - we put the begin within+; an if statement, because in this context definitions are+; not allowed. This prevents one from interfering with macro+; hygiene. +;+; TODO: unfortunately the macro logic has not yet been+; updated to take this into acccount, so the pitfall+; still fails+;+     (if #t (begin result1 result2 ...)))+    ((cond (test => result))+     (let ((temp test))+       (if temp (result temp))))+    ((cond (test => result) clause1 clause2 ...)+     (let ((temp test))+       (if temp+           (result temp)+           (cond clause1 clause2 ...))))+    ((cond (test)) test)+    ((cond (test) clause1 clause2 ...)+     (let ((temp test))+       (if temp+           temp+           (cond clause1 clause2 ...))))+    ((cond (test result1 result2 ...))+     (if test (begin result1 result2 ...)))+    ((cond (test result1 result2 ...)+           clause1 clause2 ...)+     (if test+         (begin result1 result2 ...)+         (cond clause1 clause2 ...)))))+; Case+; Form from R5RS:+(define-syntax case+  (syntax-rules (else)+    ((case (key ...)+       clauses ...)+     (let ((atom-key (key ...)))+       (case atom-key clauses ...)))+    ((case key+       (else result1 result2 ...))+     (if #t (begin result1 result2 ...)))+    ((case key+       ((atoms ...) result1 result2 ...))+     (if (memv key '(atoms ...))+         (begin result1 result2 ...)))+    ((case key+       ((atoms ...) result1 result2 ...)+       clause clauses ...)+     (if (memv key '(atoms ...))+         (begin result1 result2 ...)+         (case key clause clauses ...)))))++(define (my-mem-helper obj lst cmp-proc)+ (cond +   ((null? lst) #f)+   ((cmp-proc obj (car lst)) lst)+   (else (my-mem-helper obj (cdr lst) cmp-proc))))+(define (memq obj lst) (my-mem-helper obj lst eq?))+(define (memv obj lst) (my-mem-helper obj lst eqv?))+(define (member obj lst) (my-mem-helper obj lst equal?))++(define (mem-helper pred op)  (lambda (next acc) (if (and (not acc) (pred (op next))) next acc)))+(define (assq obj alist)      (foldl (mem-helper (curry eq? obj) car) #f alist))+(define (assv obj alist)      (foldl (mem-helper (curry eqv? obj) car) #f alist))+(define (assoc obj alist)     (foldl (mem-helper (curry equal? obj) car) #f alist))++; FUTURE: on map and for-each - Support variable number of args, per spec:+; http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_sec_6.4+;(define (for-each func . lsts) )++(define (for-each func lst) +  (if (eq? 1 (length lst))+	(func (car lst))+    (begin (func (car lst))+           (for-each func (cdr lst)))))++(define (map func lst)        (foldr (lambda (x y) (cons (func x) y)) '() lst))++(define (list-tail lst k) +        (if (zero? k)+          lst+          (list-tail (cdr lst) (- k 1))))+(define (list-ref lst k)  (car (list-tail lst k)))++; Let forms+;+; letrec from R5RS+(define-syntax letrec+    (syntax-rules ()+      ((letrec ((var1 init1) ...) body ...)+       (letrec "generate_temp_names"+         (var1 ...)+         ()+         ((var1 init1) ...)+         body ...))+      ((letrec "generate_temp_names"+         ()+         (temp1 ...)+         ((var1 init1) ...)+         body ...)                         ; start the changed code+       (let ((var1 #f) ...)+         (let ((temp1 init1) ...)+           (set! var1 temp1)+           ...+           body ...)))+      ((letrec "generate_temp_names"+         (x y ...)+         (temp ...)+         ((var1 init1) ...)+         body ...)+       (letrec "generate_temp_names"+         (y ...)+         (newtemp temp ...)+         ((var1 init1) ...)+         body ...))))++; let and named let (using the Y-combinator):+(define-syntax let+  (syntax-rules ()+    ((_ ((x v) ...) e1 e2 ...)+     ((lambda (x ...) e1 e2 ...) v ...))+    ((_ name ((x v) ...) e1 e2 ...)+     (let*+       ((f  (lambda (name)+              (lambda (x ...) e1 e2 ...)))+        (ff ((lambda (proc) (f (lambda (x ...) ((proc proc)+               x ...))))+             (lambda (proc) (f (lambda (x ...) ((proc proc)+               x ...)))))))+        (ff v ...)))))++;+; It would be nice to change first rule back to:+;    ((_ () body) body)+;+(define-syntax let*+  (syntax-rules ()+    ((let* () body1 body2 ...)+     (let () body1 body2 ...))+    ((let* ((name1 val1) (name2 val2) ...)+       body1 body2 ...)+     (let ((name1 val1))+       (let* ((name2 val2) ...)+         body1 body2 ...)))))++; Iteration - do+(define-syntax do+  (syntax-rules ()+    ((do ((var init step ...) ...)+         (test expr ...)+         command ...)+     (letrec+       ((loop+         (lambda (var ...)+           (if test+               (begin+                 (if #f #f)+                 expr ...)+               (begin+                 command+                 ...+                 (loop (do "step" var step ...)+                       ...))))))+       (loop init ...)))+    ((do "step" x)+     x)+    ((do "step" x y)+     y)))++; Delayed evaluation functions+(define force+    (lambda (object)+	      (object)))++(define-syntax delay +  (syntax-rules () +    ((delay expression)+     (make-promise (lambda () expression)))))++(define make-promise+  (lambda (proc)+    (let ((result-ready? #f)+          (result #f))+      (lambda ()+        (if result-ready? +            result+            (let ((x (proc)))+              (if result-ready?+                  result+                  (begin (set! result x)+                         (set! result-ready? #t)+                         result))))))))++; End delayed evaluation section++; String Section+(define-syntax string-fill!+  (syntax-rules ()+    ((_ _str _chr)+     (set! _str+           (make-string (string-length _str) _chr)))))++(define (string-concatenate l) +    (apply string-append l)) ++; Vector Section+(define-syntax vector-fill!+  (syntax-rules ()+    ((_ _vec _fill)+     (set! _vec+           (make-vector (vector-length _vec) _fill)))))++; Continuation Section+(define (values . things)+    (call-with-current-continuation +        (lambda (cont) (apply cont things))))++;; I/O Section+(define (newline . port)+  (if (null? port) +      (display #\newline) +      (display #\newline port)))++; TODO: test these forms+(define (call-with-input-file filename proc)+  (let ((opened-file (open-input-file filename)))+    (define result+           (proc opened-file))+    (close-input-port opened-file)+    result))+; TODO: test+(define (call-with-output-file filename proc)+  (let ((opened-file (open-output-file filename)))+    (define result+           (proc opened-file))+    (close-output-port opened-file)+    result))++;; SRFI 23 - Error reporting mechanism+;; based on code from: http://srfi.schemers.org/srfi-23/srfi-23.html+(define (error reason . args)+    (display "Error: ")+    (display reason)+    (newline)+    (for-each (lambda (arg) +                (display " ")+      	  (write arg))+      	args)+    (newline)+    (exit-fail))++;; Hashtable derived forms+(define hash-table-walk+  (lambda (ht proc)+    (map +      (lambda (kv) (proc (car kv) (car (reverse kv))))+      (hash-table->alist ht)))) ++(define (hash-table-update! hash-table key function)+  (hash-table-set! hash-table key+                  (function (hash-table-ref hash-table key thunk))))++(define-syntax hash-table-merge!+  (syntax-rules ()+    ((_ hdest hsrc)+     (map (lambda (node) (hash-table-set! hdest +                                       (car node)+                                       (cadr node)))+       (hash-table->alist hsrc)))))++(define (alist->hash-table lst)+ (let ((ht (make-hash-table)))+   (for-each (lambda (node)+              (hash-table-set! ht (car node) (cadr node)))+             lst)+   ht))++(define (hash-table-fold hash-table f acc-in)+  (let ((acc acc-in))+    (hash-table-walk hash-table +             (lambda (key value) (set! acc (f key value acc))))+      acc))++; Implementations of gcd and lcm using Euclid's algorithm+;+; Also note that each form is written to accept either 0 or+; 2 arguments, per R5RS. This could probably be generalized+; even further, if necessary.+;+(define gcd '())+(define lcm '())++(let ()+  ; Main GCD algorithm+  (define (gcd/main a b)+    (if (= b 0)+      (abs a)+      (gcd/main b (modulo a b))))++  ; A helper function to reduce the input list+  (define (gcd/entry . nums)+    (if (eqv? nums '())+      0+      (foldl gcd/main (car nums) (cdr nums))))++  ; Main LCM algorithm+  (define (lcm/main a b)+    (abs (/ (* a b) (gcd/main a b))))++  ; A helper function to reduce the input list+  (define (lcm/entry . nums)+    (if (eqv? nums '())+      1+      (foldl lcm/main (car nums) (cdr nums))))++  (set! gcd gcd/entry)+  (set! lcm lcm/entry))  ++; SRFI 8+; Reference implementation from: http://srfi.schemers.org/srfi-8/srfi-8.html+;+; FUTURE: This may be moved into its own file+;+(define-syntax receive+    (syntax-rules ()+        ((receive formals expression body ...)+         (call-with-values (lambda () expression)+             (lambda formals body ...)))))+; END SRFI 8++; append accepts a variable number of arguments, per R5RS. So a wrapper+; has been provided for the standard 2-argument version of (append).+;+; We return the given value if less than 2 arguments are given, and+; otherwise fold over each arg, appending it to its predecessor. +(define (append . lst)+  (define append-2+          (lambda (inlist alist) +                  (foldr (lambda (ap in) (cons ap in)) alist inlist)))+  (if (null? lst)+      lst+      (if (null? (cdr lst))+          (car lst)+          (foldl (lambda (a b) (append-2 b a)) (car lst) (cdr lst)))))++; Quasi-quotation as a macro+; Based on code from chibi-scheme+;+; The code below is compiled to avoid having to expand dozens of macros+; in real-time, significantly improving performance. This does highlight+; an area that husk could improve upon but for now a compilation will do.+;+; The (expand) special form is used to compile the code, in case it needs+; to be changed in the future.+;+(define-syntax quasiquote+  (er-macro-transformer+   (lambda (expr rename compare)+(define (qq x d) (if (pair? x) ((lambda () (if (compare (rename (quote unquote)) (car x)) ((lambda () (if (<= d 0) (cadr x) (list (rename (quote list)) (list (rename (quote quote)) (quote unquote)) (qq (cadr x) (- d 1)))))) (if (compare (rename (quote unquote-splicing)) (car x)) ((lambda () (if (<= d 0) (list (rename (quote cons)) (qq (car x) d) (qq (cdr x) d)) (list (rename (quote list)) (list (rename (quote quote)) (quote unquote-splicing)) (qq (cadr x) (- d 1)))))) (if (compare (rename (quote quasiquote)) (car x)) ((lambda () (list (rename (quote list)) (list (rename (quote quote)) (quote quasiquote)) (qq (cadr x) (+ d 1))))) (if (if (<= d 0) (if (pair? (car x)) (compare (rename (quote unquote-splicing)) (caar x)) #f) #f) ((lambda () (if (null? (cdr x)) (cadr (car x)) (list (rename (quote append)) (cadr (car x)) (qq (cdr x) d))))) (if #t ((lambda () (list (rename (quote cons)) (qq (car x) d) (qq (cdr x) d))))))))))) (if (vector? x) ((lambda () (list (rename (quote list->vector)) (qq (vector->list x) d)))) (if (if (symbol? x) #t (null? x)) ((lambda () (list (rename (quote quote)) x))) (if #t ((lambda () x)))))))+     (qq (cadr expr) 0))))+;; Original code:+;     (define (qq x d)+;       (cond+;        ((pair? x)+;         (cond+;          ((compare (rename 'unquote) (car x))+;           (if (<= d 0)+;               (cadr x)+;               (list (rename 'list) (list (rename 'quote) 'unquote)+;                     (qq (cadr x) (- d 1)))))+;          ((compare (rename 'unquote-splicing) (car x))+;           (if (<= d 0)+;               (list (rename 'cons) (qq (car x) d) (qq (cdr x) d))+;               (list (rename 'list) (list (rename 'quote) 'unquote-splicing)+;                     (qq (cadr x) (- d 1)))))+;          ((compare (rename 'quasiquote) (car x))+;           (list (rename 'list) (list (rename 'quote) 'quasiquote)+;                 (qq (cadr x) (+ d 1))))+;          ((and (<= d 0) (pair? (car x))+;                (compare (rename 'unquote-splicing) (caar x)))+;           (if (null? (cdr x))+;               (cadr (car x))+;               (list (rename 'append) (cadr (car x)) (qq (cdr x) d))))+;          (else+;           (list (rename 'cons) (qq (car x) d) (qq (cdr x) d)))))+;        ((vector? x) (list (rename 'list->vector) (qq (vector->list x) d)))+;        ((if (symbol? x) #t (null? x)) (list (rename 'quote) x))+;        (else x)))+;     (qq (cadr expr) 0))))
− srfi/srfi-1.scm
@@ -1,1664 +0,0 @@-;;;-;;;-;;; husk-scheme-;;; http://justinethier.github.com/husk-scheme-;;;-;;;-;;;-;;; Shiver's reference implementation of SRFI 1 from -;;; http://srfi.schemers.org/srfi-1/srfi-1-reference.scm-;;; Copyright (c) 1998, 1999 by Olin Shivers.-;;;-;;;-;;; Implementation of SRFI-1: List Library-;;;-;;; This file contains all of the SRFI-1 functions-;;; that are not part of the standard library.-;;;---;;; SRFI-1 list-processing library 			-*- Scheme -*--;;; Reference implementation-;;;-;;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with-;;; this code as long as you do not remove this copyright notice or-;;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.-;;;     -Olin--;;; This is a library of list- and pair-processing functions. I wrote it after-;;; carefully considering the functions provided by the libraries found in-;;; R4RS/R5RS Scheme, MIT Scheme, Gambit, RScheme, MzScheme, slib, Common-;;; Lisp, Bigloo, guile, T, APL and the SML standard basis. It is a pretty-;;; rich toolkit, providing a superset of the functionality found in any of-;;; the various Schemes I considered.--;;; This implementation is intended as a portable reference implementation-;;; for SRFI-1. See the porting notes below for more information.--;;; Exported:-;;; xcons tree-copy make-list list-tabulate cons* list-copy -;;; proper-list? circular-list? dotted-list? not-pair? null-list? list=-;;; circular-list length+-;;; iota-;;; first second third fourth fifth sixth seventh eighth ninth tenth-;;; car+cdr-;;; take       drop       -;;; take-right drop-right -;;; take!      drop-right!-;;; split-at   split-at!-;;; last last-pair-;;; zip unzip1 unzip2 unzip3 unzip4 unzip5-;;; count-;;; append! append-reverse append-reverse! concatenate concatenate! -;;; unfold       fold       pair-fold       reduce-;;; unfold-right fold-right pair-fold-right reduce-right-;;; append-map append-map! map! pair-for-each filter-map map-in-order-;;; filter  partition  remove-;;; filter! partition! remove! -;;; find find-tail any every list-index-;;; take-while drop-while take-while!-;;; span break span! break!-;;; delete delete!-;;; alist-cons alist-copy-;;; delete-duplicates delete-duplicates!-;;; alist-delete alist-delete!-;;; reverse! -;;; lset<= lset= lset-adjoin  -;;; lset-union  lset-intersection  lset-difference  lset-xor  lset-diff+intersection-;;; lset-union! lset-intersection! lset-difference! lset-xor! lset-diff+intersection!-;;; -;;; In principle, the following R4RS list- and pair-processing procedures-;;; are also part of this package's exports, although they are not defined-;;; in this file:-;;;   Primitives: cons pair? null? car cdr set-car! set-cdr!-;;;   Non-primitives: list length append reverse cadr ... cddddr list-ref-;;;                   memq memv assq assv-;;;   (The non-primitives are defined in this file, but commented out.)-;;;-;;; These R4RS procedures have extended definitions in SRFI-1 and are defined-;;; in this file:-;;;   map for-each member assoc-;;;-;;; The remaining two R4RS list-processing procedures are not included: -;;;   list-tail (use drop)-;;;   list? (use proper-list?)---;;; A note on recursion and iteration/reversal:-;;; Many iterative list-processing algorithms naturally compute the elements-;;; of the answer list in the wrong order (left-to-right or head-to-tail) from-;;; the order needed to cons them into the proper answer (right-to-left, or-;;; tail-then-head). One style or idiom of programming these algorithms, then,-;;; loops, consing up the elements in reverse order, then destructively -;;; reverses the list at the end of the loop. I do not do this. The natural-;;; and efficient way to code these algorithms is recursively. This trades off-;;; intermediate temporary list structure for intermediate temporary stack-;;; structure. In a stack-based system, this improves cache locality and-;;; lightens the load on the GC system. Don't stand on your head to iterate!-;;; Recurse, where natural. Multiple-value returns make this even more-;;; convenient, when the recursion/iteration has multiple state values.--;;; Porting:-;;; This is carefully tuned code; do not modify casually.-;;;   - It is careful to share storage when possible;-;;;   - Side-effecting code tries not to perform redundant writes.-;;; -;;; That said, a port of this library to a specific Scheme system might wish-;;; to tune this code to exploit particulars of the implementation. -;;; The single most important compiler-specific optimisation you could make-;;; to this library would be to add rewrite rules or transforms to:-;;; - transform applications of n-ary procedures (e.g. LIST=, CONS*, APPEND,-;;;   LSET-UNION) into multiple applications of a primitive two-argument -;;;   variant.-;;; - transform applications of the mapping functions (MAP, FOR-EACH, FOLD, -;;;   ANY, EVERY) into open-coded loops. The killer here is that these -;;;   functions are n-ary. Handling the general case is quite inefficient,-;;;   requiring many intermediate data structures to be allocated and-;;;   discarded.-;;; - transform applications of procedures that take optional arguments-;;;   into calls to variants that do not take optional arguments. This-;;;   eliminates unnecessary consing and parsing of the rest parameter.-;;;-;;; These transforms would provide BIG speedups. In particular, the n-ary-;;; mapping functions are particularly slow and cons-intensive, and are good-;;; candidates for tuning. I have coded fast paths for the single-list cases,-;;; but what you really want to do is exploit the fact that the compiler-;;; usually knows how many arguments are being passed to a particular-;;; application of these functions -- they are usually explicitly called, not-;;; passed around as higher-order values. If you can arrange to have your-;;; compiler produce custom code or custom linkages based on the number of-;;; arguments in the call, you can speed these functions up a *lot*. But this-;;; kind of compiler technology no longer exists in the Scheme world as far as-;;; I can see.-;;;-;;; Note that this code is, of course, dependent upon standard bindings for-;;; the R5RS procedures -- i.e., it assumes that the variable CAR is bound-;;; to the procedure that takes the car of a list. If your Scheme -;;; implementation allows user code to alter the bindings of these procedures-;;; in a manner that would be visible to these definitions, then there might-;;; be trouble. You could consider horrible kludgery along the lines of-;;;    (define fact -;;;      (let ((= =) (- -) (* *))-;;;        (letrec ((real-fact (lambda (n) -;;;                              (if (= n 0) 1 (* n (real-fact (- n 1)))))))-;;;          real-fact)))-;;; Or you could consider shifting to a reasonable Scheme system that, say,-;;; has a module system protecting code from this kind of lossage.-;;;-;;; This code does a fair amount of run-time argument checking. If your-;;; Scheme system has a sophisticated compiler that can eliminate redundant-;;; error checks, this is no problem. However, if not, these checks incur-;;; some performance overhead -- and, in a safe Scheme implementation, they-;;; are in some sense redundant: if we don't check to see that the PROC -;;; parameter is a procedure, we'll find out anyway three lines later when-;;; we try to call the value. It's pretty easy to rip all this argument -;;; checking code out if it's inappropriate for your implementation -- just-;;; nuke every call to CHECK-ARG.-;;;-;;; On the other hand, if you *do* have a sophisticated compiler that will-;;; actually perform soft-typing and eliminate redundant checks (Rice's systems-;;; being the only possible candidate of which I'm aware), leaving these checks -;;; in can *help*, since their presence can be elided in redundant cases,-;;; and in cases where they are needed, performing the checks early, at-;;; procedure entry, can "lift" a check out of a loop. -;;;-;;; Finally, I have only checked the properties that can portably be checked-;;; with R5RS Scheme -- and this is not complete. You may wish to alter-;;; the CHECK-ARG parameter checks to perform extra, implementation-specific-;;; checks, such as procedure arity for higher-order values.-;;;-;;; The code has only these non-R4RS dependencies:-;;;   A few calls to an ERROR procedure;-;;;   Uses of the R5RS multiple-value procedure VALUES and the m-v binding-;;;     RECEIVE macro (which isn't R5RS, but is a trivial macro).-;;;   Many calls to a parameter-checking procedure check-arg:-;;;    (define (check-arg pred val caller)-;;;      (let lp ((val val))-;;;        (if (pred val) val (lp (error "Bad argument" val pred caller)))))-;;;   A few uses of the LET-OPTIONAL and :OPTIONAL macros for parsing-;;;     optional arguments.-;;;-;;; Most of these procedures use the NULL-LIST? test to trigger the-;;; base case in the inner loop or recursion. The NULL-LIST? function-;;; is defined to be a careful one -- it raises an error if passed a-;;; non-nil, non-pair value. The spec allows an implementation to use-;;; a less-careful implementation that simply defines NULL-LIST? to-;;; be NOT-PAIR?. This would speed up the inner loops of these procedures-;;; at the expense of having them silently accept dotted lists.--;;; A note on dotted lists:-;;; I, personally, take the view that the only consistent view of lists-;;; in Scheme is the view that *everything* is a list -- values such as-;;; 3 or "foo" or 'bar are simply empty dotted lists. This is due to the-;;; fact that Scheme actually has no true list type. It has a pair type,-;;; and there is an *interpretation* of the trees built using this type-;;; as lists.-;;;-;;; I lobbied to have these list-processing procedures hew to this-;;; view, and accept any value as a list argument. I was overwhelmingly-;;; overruled during the SRFI discussion phase. So I am inserting this-;;; text in the reference lib and the SRFI spec as a sort of "minority-;;; opinion" dissent.-;;;-;;; Many of the procedures in this library can be trivially redefined-;;; to handle dotted lists, just by changing the NULL-LIST? base-case-;;; check to NOT-PAIR?, meaning that any non-pair value is taken to be-;;; an empty list. For most of these procedures, that's all that is-;;; required.-;;;-;;; However, we have to do a little more work for some procedures that-;;; *produce* lists from other lists.  Were we to extend these procedures to-;;; accept dotted lists, we would have to define how they terminate the lists-;;; produced as results when passed a dotted list. I designed a coherent set-;;; of termination rules for these cases; this was posted to the SRFI-1-;;; discussion list. I additionally wrote an earlier version of this library-;;; that implemented that spec. It has been discarded during later phases of-;;; the definition and implementation of this library.-;;;-;;; The argument *against* defining these procedures to work on dotted-;;; lists is that dotted lists are the rare, odd case, and that by -;;; arranging for the procedures to handle them, we lose error checking-;;; in the cases where a dotted list is passed by accident -- e.g., when-;;; the programmer swaps a two arguments to a list-processing function,-;;; one being a scalar and one being a list. For example,-;;;     (member '(1 3 5 7 9) 7)-;;; This would quietly return #f if we extended MEMBER to accept dotted-;;; lists.-;;;-;;; The SRFI discussion record contains more discussion on this topic.---; JAE - These are the necessary helper constructs-; -; TBD: Should these be moved to another file, and/or-;      encapsulated to only this srfi ??-(define (check-arg pred val caller)-  (let lp ((val val))-    (if (pred val) val (lp (error "Bad argument" val pred caller)))))--; TODO: unfortunately the original version of this was written using an-; explicit-renaming low level macro system, so it is not portable to R5RS.-; See: http://www.scsh.net/mail-archive/scsh-users/1996-04/msg00010.html-;-;;; (LET-OPTIONALS args ((var1 default1) ...) body1 ...)-;;; ;;; The expander is defined in the code above.-;-;(define-syntax let-optionals expand-let-optionals)--;;; (:optional rest-arg default-exp)-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;; This form is for evaluating optional arguments and their defaults-;;; in simple procedures that take a *single* optional argument. It is-;;; a macro so that the default will not be computed unless it is needed.-;;; -;;; REST-ARG is a rest list from a lambda -- e.g., R in-;;;     (lambda (a b . r) ...)-;;; - If REST-ARG has 0 elements, evaluate DEFAULT-EXP and return that.-;;; - If REST-ARG has 1 element, return that element.-;;; - If REST-ARG has >1 element, error.--(define-syntax :optional-  (syntax-rules ()-    ((:optional rest default-exp)-     (let ((maybe-arg rest))-       (cond ((null? maybe-arg) default-exp)-             ((null? (cdr maybe-arg)) (car maybe-arg))-             (else (error "too many optional arguments" maybe-arg)))))))---;;; Constructors-;;;;;;;;;;;;;;;;--;;; Occasionally useful as a value to be passed to a fold or other-;;; higher-order procedure.-(define (xcons d a) (cons a d))--;;;; Recursively copy every cons.-;(define (tree-copy x)-;  (let recur ((x x))-;    (if (not (pair? x)) x-;	(cons (recur (car x)) (recur (cdr x))))))--;;; Make a list of length LEN.--(define (make-list len . maybe-elt)-  (check-arg (lambda (n) (and (integer? n) (>= n 0))) len make-list)-  (let ((elt (cond ((null? maybe-elt) #f) ; Default value-		   ((null? (cdr maybe-elt)) (car maybe-elt))-		   (else (error "Too many arguments to MAKE-LIST"-				(cons len maybe-elt))))))-    (do ((i len (- i 1))-	 (ans '() (cons elt ans)))-	((<= i 0) ans))))---;(define (list . ans) ans)	; R4RS---;;; Make a list of length LEN. Elt i is (PROC i) for 0 <= i < LEN.--(define (list-tabulate len proc)-  (check-arg (lambda (n) (and (integer? n) (>= n 0))) len list-tabulate)-  (check-arg procedure? proc list-tabulate)-  (do ((i (- len 1) (- i 1))-       (ans '() (cons (proc i) ans)))-      ((< i 0) ans)))--;;; (cons* a1 a2 ... an) = (cons a1 (cons a2 (cons ... an)))-;;; (cons* a1) = a1	(cons* a1 a2 ...) = (cons a1 (cons* a2 ...))-;;;-;;; (cons first (unfold not-pair? car cdr rest values))--(define (cons* first . rest)-  (let recur ((x first) (rest rest))-    (if (pair? rest)-	(cons x (recur (car rest) (cdr rest)))-	x)))--;;; (unfold not-pair? car cdr lis values)--(define (list-copy lis)				-  (let recur ((lis lis))			-    (if (pair? lis)				-	(cons (car lis) (recur (cdr lis)))	-	lis)))					--;;; IOTA count [start step]	(start start+step ... start+(count-1)*step)--(define (iota count . maybe-start+step)-  (check-arg integer? count iota)-  (if (< count 0) (error "Negative step count" iota count))--; JAE-;-; Replaced the let-optionals macro below with code that is-; almost equivalent - but the caller must specify either-; both parameters or none at all.-;-;  (let-optionals maybe-start+step ((start 0) (step 1))-  (let ((start 0) (step 1))-    (if (not (null? maybe-start+step))-      (begin-        (set! start (first maybe-start+step))-        (set! step (second maybe-start+step))))-;/JAE-    (check-arg number? start iota)-    (check-arg number? step iota)-    (let loop ((n 0) (r '()))-      (if (= n count)-	  (reverse r)-	  (loop (+ 1 n)-		(cons (+ start (* n step)) r))))))-	  -;;; I thought these were lovely, but the public at large did not share my-;;; enthusiasm...-;;; :IOTA to		(0 ... to-1)-;;; :IOTA from to	(from ... to-1)-;;; :IOTA from to step  (from from+step ...)--;;; IOTA: to		(1 ... to)-;;; IOTA: from to	(from+1 ... to)-;;; IOTA: from to step	(from+step from+2step ...)--;(define (%parse-iota-args arg1 rest-args proc)-;  (let ((check (lambda (n) (check-arg integer? n proc))))-;    (check arg1)-;    (if (pair? rest-args)-;	(let ((arg2 (check (car rest-args)))-;	      (rest (cdr rest-args)))-;	  (if (pair? rest)-;	      (let ((arg3 (check (car rest)))-;		    (rest (cdr rest)))-;		(if (pair? rest) (error "Too many parameters" proc arg1 rest-args)-;		    (values arg1 arg2 arg3)))-;	      (values arg1 arg2 1)))-;	(values 0 arg1 1))))-;-;(define (iota: arg1 . rest-args)-;  (receive (from to step) (%parse-iota-args arg1 rest-args iota:)-;    (let* ((numsteps (floor (/ (- to from) step)))-;	   (last-val (+ from (* step numsteps))))-;      (if (< numsteps 0) (error "Negative step count" iota: from to step))-;      (do ((steps-left numsteps (- steps-left 1))-;	   (val last-val (- val step))-;	   (ans '() (cons val ans)))-;	  ((<= steps-left 0) ans)))))-;-;-;(define (:iota arg1 . rest-args)-;  (receive (from to step) (%parse-iota-args arg1 rest-args :iota)-;    (let* ((numsteps (ceiling (/ (- to from) step)))-;	   (last-val (+ from (* step (- numsteps 1)))))-;      (if (< numsteps 0) (error "Negative step count" :iota from to step))-;      (do ((steps-left numsteps (- steps-left 1))-;	   (val last-val (- val step))-;	   (ans '() (cons val ans)))-;	  ((<= steps-left 0) ans)))))----(define (circular-list val1 . vals)-  (let ((ans (cons val1 vals)))-    (set-cdr! (last-pair ans) ans)-    ans))--;;; <proper-list> ::= ()			; Empty proper list-;;;		  |   (cons <x> <proper-list>)	; Proper-list pair-;;; Note that this definition rules out circular lists -- and this-;;; function is required to detect this case and return false.--(define (proper-list? x)-  (let lp ((x x) (lag x))-    (if (pair? x)-	(let ((x (cdr x)))-	  (if (pair? x)-	      (let ((x   (cdr x))-		    (lag (cdr lag)))-		(and (not (eq? x lag)) (lp x lag)))-	      (null? x)))-	(null? x))))---;;; A dotted list is a finite list (possibly of length 0) terminated-;;; by a non-nil value. Any non-cons, non-nil value (e.g., "foo" or 5)-;;; is a dotted list of length 0.-;;;-;;; <dotted-list> ::= <non-nil,non-pair>	; Empty dotted list-;;;               |   (cons <x> <dotted-list>)	; Proper-list pair--(define (dotted-list? x)-  (let lp ((x x) (lag x))-    (if (pair? x)-	(let ((x (cdr x)))-	  (if (pair? x)-	      (let ((x   (cdr x))-		    (lag (cdr lag)))-		(and (not (eq? x lag)) (lp x lag)))-	      (not (null? x))))-	(not (null? x)))))--(define (circular-list? x)-  (let lp ((x x) (lag x))-    (and (pair? x)-	 (let ((x (cdr x)))-	   (and (pair? x)-		(let ((x   (cdr x))-		      (lag (cdr lag)))-		  (or (eq? x lag) (lp x lag))))))))--(define (not-pair? x) (not (pair? x)))	; Inline me.--;;; This is a legal definition which is fast and sloppy:-;;;     (define null-list? not-pair?)-;;; but we'll provide a more careful one:-(define (null-list? l)-  (cond ((pair? l) #f)-	((null? l) #t)-	(else (error "null-list?: argument out of domain" l))))-           --(define (list= = . lists)-  (or (null? lists) ; special case--      (let lp1 ((list-a (car lists)) (others (cdr lists)))-	(or (null? others)-	    (let ((list-b (car others))-		  (others (cdr others)))-	      (if (eq? list-a list-b)	; EQ? => LIST=-		  (lp1 list-b others)-		  (let lp2 ((list-a list-a) (list-b list-b))-		    (if (null-list? list-a)-			(and (null-list? list-b)-			     (lp1 list-b others))-			(and (not (null-list? list-b))-			     (= (car list-a) (car list-b))-			     (lp2 (cdr list-a) (cdr list-b)))))))))))-			---;;; R4RS, so commented out.-;(define (length x)			; LENGTH may diverge or-;  (let lp ((x x) (len 0))		; raise an error if X is-;    (if (pair? x)			; a circular list. This version-;        (lp (cdr x) (+ len 1))		; diverges.-;        len)))--(define (length+ x)			; Returns #f if X is circular.-  (let lp ((x x) (lag x) (len 0))-    (if (pair? x)-	(let ((x (cdr x))-	      (len (+ len 1)))-	  (if (pair? x)-	      (let ((x   (cdr x))-		    (lag (cdr lag))-		    (len (+ len 1)))-		(and (not (eq? x lag)) (lp x lag len)))-	      len))-	len)))--(define (zip list1 . more-lists) (apply map list list1 more-lists))---;;; Selectors-;;;;;;;;;;;;;--;;; R4RS non-primitives:-;(define (caar   x) (car (car x)))-;(define (cadr   x) (car (cdr x)))-;(define (cdar   x) (cdr (car x)))-;(define (cddr   x) (cdr (cdr x)))-;-;(define (caaar  x) (caar (car x)))-;(define (caadr  x) (caar (cdr x)))-;(define (cadar  x) (cadr (car x)))-;(define (caddr  x) (cadr (cdr x)))-;(define (cdaar  x) (cdar (car x)))-;(define (cdadr  x) (cdar (cdr x)))-;(define (cddar  x) (cddr (car x)))-;(define (cdddr  x) (cddr (cdr x)))-;-;(define (caaaar x) (caaar (car x)))-;(define (caaadr x) (caaar (cdr x)))-;(define (caadar x) (caadr (car x)))-;(define (caaddr x) (caadr (cdr x)))-;(define (cadaar x) (cadar (car x)))-;(define (cadadr x) (cadar (cdr x)))-;(define (caddar x) (caddr (car x)))-;(define (cadddr x) (caddr (cdr x)))-;(define (cdaaar x) (cdaar (car x)))-;(define (cdaadr x) (cdaar (cdr x)))-;(define (cdadar x) (cdadr (car x)))-;(define (cdaddr x) (cdadr (cdr x)))-;(define (cddaar x) (cddar (car x)))-;(define (cddadr x) (cddar (cdr x)))-;(define (cdddar x) (cdddr (car x)))-;(define (cddddr x) (cdddr (cdr x)))---(define first  car)-(define second cadr)-(define third  caddr)-(define fourth cadddr)-(define (fifth   x) (car    (cddddr x)))-(define (sixth   x) (cadr   (cddddr x)))-(define (seventh x) (caddr  (cddddr x)))-(define (eighth  x) (cadddr (cddddr x)))-(define (ninth   x) (car  (cddddr (cddddr x))))-(define (tenth   x) (cadr (cddddr (cddddr x))))--(define (car+cdr pair) (values (car pair) (cdr pair)))--;;; take & drop--(define (take lis k)-  (check-arg integer? k take)-  (let recur ((lis lis) (k k))-    (if (zero? k) '()-	(cons (car lis)-	      (recur (cdr lis) (- k 1))))))--(define (drop lis k)-  (check-arg integer? k drop)-  (let iter ((lis lis) (k k))-    (if (zero? k) lis (iter (cdr lis) (- k 1)))))--(define (take! lis k)-  (check-arg integer? k take!)-  (if (zero? k) '()-      (begin (set-cdr! (drop lis (- k 1)) '())-	     lis)))--;;; TAKE-RIGHT and DROP-RIGHT work by getting two pointers into the list, -;;; off by K, then chasing down the list until the lead pointer falls off-;;; the end.--(define (take-right lis k)-  (check-arg integer? k take-right)-  (let lp ((lag lis)  (lead (drop lis k)))-    (if (pair? lead)-	(lp (cdr lag) (cdr lead))-	lag)))--(define (drop-right lis k)-  (check-arg integer? k drop-right)-  (let recur ((lag lis) (lead (drop lis k)))-    (if (pair? lead)-	(cons (car lag) (recur (cdr lag) (cdr lead)))-	'())))--;;; In this function, LEAD is actually K+1 ahead of LAG. This lets-;;; us stop LAG one step early, in time to smash its cdr to ().-(define (drop-right! lis k)-  (check-arg integer? k drop-right!)-  (let ((lead (drop lis k)))-    (if (pair? lead)--	(let lp ((lag lis)  (lead (cdr lead)))	; Standard case-	  (if (pair? lead)-	      (lp (cdr lag) (cdr lead))-	      (begin (set-cdr! lag '())-		     lis)))--	'())))	; Special case dropping everything -- no cons to side-effect.--;(define (list-ref lis i) (car (drop lis i)))	; R4RS--;;; These use the APL convention, whereby negative indices mean -;;; "from the right." I liked them, but they didn't win over the-;;; SRFI reviewers.-;;; K >= 0: Take and drop  K elts from the front of the list.-;;; K <= 0: Take and drop -K elts from the end   of the list.--;(define (take lis k)-;  (check-arg integer? k take)-;  (if (negative? k)-;      (list-tail lis (+ k (length lis)))-;      (let recur ((lis lis) (k k))-;	(if (zero? k) '()-;	    (cons (car lis)-;		  (recur (cdr lis) (- k 1)))))))-;-;(define (drop lis k)-;  (check-arg integer? k drop)-;  (if (negative? k)-;      (let recur ((lis lis) (nelts (+ k (length lis))))-;	(if (zero? nelts) '()-;	    (cons (car lis)-;		  (recur (cdr lis) (- nelts 1)))))-;      (list-tail lis k)))-;-;-;(define (take! lis k)-;  (check-arg integer? k take!)-;  (cond ((zero? k) '())-;	((positive? k)-;	 (set-cdr! (list-tail lis (- k 1)) '())-;	 lis)-;	(else (list-tail lis (+ k (length lis))))))-;-;(define (drop! lis k)-;  (check-arg integer? k drop!)-;  (if (negative? k)-;      (let ((nelts (+ k (length lis))))-;	(if (zero? nelts) '()-;	    (begin (set-cdr! (list-tail lis (- nelts 1)) '())-;		   lis)))-;      (list-tail lis k)))--(define (split-at x k)-  (check-arg integer? k split-at)-  (let recur ((lis x) (k k))-    (if (zero? k) (values '() lis)-	(receive (prefix suffix) (recur (cdr lis) (- k 1))-	  (values (cons (car lis) prefix) suffix)))))--(define (split-at! x k)-  (check-arg integer? k split-at!)-  (if (zero? k) (values '() x)-      (let* ((prev (drop x (- k 1)))-	     (suffix (cdr prev)))-	(set-cdr! prev '())-	(values x suffix))))---(define (last lis) (car (last-pair lis)))--(define (last-pair lis)-  (check-arg pair? lis last-pair)-  (let lp ((lis lis))-    (let ((tail (cdr lis)))-      (if (pair? tail) (lp tail) lis))))---;;; Unzippers -- 1 through 5-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;--(define (unzip1 lis) (map car lis))--(define (unzip2 lis)-  (let recur ((lis lis))-    (if (null-list? lis) (values lis lis)	; Use NOT-PAIR? to handle-	(let ((elt (car lis)))			; dotted lists.-	  (receive (a b) (recur (cdr lis))-	    (values (cons (car  elt) a)-		    (cons (cadr elt) b)))))))--(define (unzip3 lis)-  (let recur ((lis lis))-    (if (null-list? lis) (values lis lis lis)-	(let ((elt (car lis)))-	  (receive (a b c) (recur (cdr lis))-	    (values (cons (car   elt) a)-		    (cons (cadr  elt) b)-		    (cons (caddr elt) c)))))))--(define (unzip4 lis)-  (let recur ((lis lis))-    (if (null-list? lis) (values lis lis lis lis)-	(let ((elt (car lis)))-	  (receive (a b c d) (recur (cdr lis))-	    (values (cons (car    elt) a)-		    (cons (cadr   elt) b)-		    (cons (caddr  elt) c)-		    (cons (cadddr elt) d)))))))--(define (unzip5 lis)-  (let recur ((lis lis))-    (if (null-list? lis) (values lis lis lis lis lis)-	(let ((elt (car lis)))-	  (receive (a b c d e) (recur (cdr lis))-	    (values (cons (car     elt) a)-		    (cons (cadr    elt) b)-		    (cons (caddr   elt) c)-		    (cons (cadddr  elt) d)-		    (cons (car (cddddr  elt)) e)))))))---;;; append! append-reverse append-reverse! concatenate concatenate!-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;--(define (append! . lists)-  ;; First, scan through lists looking for a non-empty one.-  (let lp ((lists lists) (prev '()))-    (if (not (pair? lists)) prev-	(let ((first (car lists))-	      (rest (cdr lists)))-	  (if (not (pair? first)) (lp rest first)--	      ;; Now, do the splicing.-	      (let lp2 ((tail-cons (last-pair first))-			(rest rest))-		(if (pair? rest)-		    (let ((next (car rest))-			  (rest (cdr rest)))-		      (set-cdr! tail-cons next)-		      (lp2 (if (pair? next) (last-pair next) tail-cons)-			   rest))-		    first)))))))--;;; APPEND is R4RS.-;(define (append . lists)-;  (if (pair? lists)-;      (let recur ((list1 (car lists)) (lists (cdr lists)))-;        (if (pair? lists)-;            (let ((tail (recur (car lists) (cdr lists))))-;              (fold-right cons tail list1)) ; Append LIST1 & TAIL.-;            list1))-;      '()))--;(define (append-reverse rev-head tail) (fold cons tail rev-head))--;(define (append-reverse! rev-head tail)-;  (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair)-;             tail-;             rev-head))--;;; Hand-inline the FOLD and PAIR-FOLD ops for speed.--(define (append-reverse rev-head tail)-  (let lp ((rev-head rev-head) (tail tail))-    (if (null-list? rev-head) tail-	(lp (cdr rev-head) (cons (car rev-head) tail)))))--(define (append-reverse! rev-head tail)-  (let lp ((rev-head rev-head) (tail tail))-    (if (null-list? rev-head) tail-	(let ((next-rev (cdr rev-head)))-	  (set-cdr! rev-head tail)-	  (lp next-rev rev-head)))))---(define (concatenate  lists) (reduce-right append  '() lists))-(define (concatenate! lists) (reduce-right append! '() lists))--;;; Fold/map internal utilities-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;; These little internal utilities are used by the general-;;; fold & mapper funs for the n-ary cases . It'd be nice if they got inlined.-;;; One the other hand, the n-ary cases are painfully inefficient as it is.-;;; An aggressive implementation should simply re-write these functions -;;; for raw efficiency; I have written them for as much clarity, portability,-;;; and simplicity as can be achieved.-;;;-;;; I use the dreaded call/cc to do local aborts. A good compiler could-;;; handle this with extreme efficiency. An implementation that provides-;;; a one-shot, non-persistent continuation grabber could help the compiler-;;; out by using that in place of the call/cc's in these routines.-;;;-;;; These functions have funky definitions that are precisely tuned to-;;; the needs of the fold/map procs -- for example, to minimize the number-;;; of times the argument lists need to be examined.--;;; Return (map cdr lists). -;;; However, if any element of LISTS is empty, just abort and return '().-(define (%cdrs lists)-  (call-with-current-continuation-    (lambda (abort)-      (let recur ((lists lists))-	(if (pair? lists)-	    (let ((lis (car lists)))-	      (if (null-list? lis) (abort '())-		  (cons (cdr lis) (recur (cdr lists)))))-	    '())))))--(define (%cars+ lists last-elt)	; (append! (map car lists) (list last-elt))-  (let recur ((lists lists))-    (if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt))))--;;; LISTS is a (not very long) non-empty list of lists.-;;; Return two lists: the cars & the cdrs of the lists.-;;; However, if any of the lists is empty, just abort and return [() ()].--(define (%cars+cdrs lists)-  (call-with-current-continuation-    (lambda (abort)-      (let recur ((lists lists))-        (if (pair? lists)-	    (receive (list other-lists) (car+cdr lists)-	      (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out-		  (receive (a d) (car+cdr list)-		    (receive (cars cdrs) (recur other-lists)-		      (values (cons a cars) (cons d cdrs))))))-	    (values '() '()))))))--;;; Like %CARS+CDRS, but we pass in a final elt tacked onto the end of the-;;; cars list. What a hack.-(define (%cars+cdrs+ lists cars-final)-  (call-with-current-continuation-    (lambda (abort)-      (let recur ((lists lists))-        (if (pair? lists)-	    (receive (list other-lists) (car+cdr lists)-	      (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out-		  (receive (a d) (car+cdr list)-		    (receive (cars cdrs) (recur other-lists)-		      (values (cons a cars) (cons d cdrs))))))-	    (values (list cars-final) '()))))))--;;; Like %CARS+CDRS, but blow up if any list is empty.-(define (%cars+cdrs/no-test lists)-  (let recur ((lists lists))-    (if (pair? lists)-	(receive (list other-lists) (car+cdr lists)-	  (receive (a d) (car+cdr list)-	    (receive (cars cdrs) (recur other-lists)-	      (values (cons a cars) (cons d cdrs)))))-	(values '() '()))))---;;; count-;;;;;;;;;-(define (count pred list1 . lists)-  (check-arg procedure? pred count)-  (if (pair? lists)--      ;; N-ary case-      (let lp ((list1 list1) (lists lists) (i 0))-	(if (null-list? list1) i-	    (receive (as ds) (%cars+cdrs lists)-	      (if (null? as) i-		  (lp (cdr list1) ds-		      (if (apply pred (car list1) as) (+ i 1) i))))))--      ;; Fast path-      (let lp ((lis list1) (i 0))-	(if (null-list? lis) i-	    (lp (cdr lis) (if (pred (car lis)) (+ i 1) i))))))---;;; fold/unfold-;;;;;;;;;;;;;;;--(define (unfold-right p f g seed . maybe-tail)-  (check-arg procedure? p unfold-right)-  (check-arg procedure? f unfold-right)-  (check-arg procedure? g unfold-right)-  (let lp ((seed seed) (ans (:optional maybe-tail '())))-    (if (p seed) ans-	(lp (g seed)-	    (cons (f seed) ans)))))---(define (unfold p f g seed . maybe-tail-gen)-  (check-arg procedure? p unfold)-  (check-arg procedure? f unfold)-  (check-arg procedure? g unfold)-  (if (pair? maybe-tail-gen)--      (let ((tail-gen (car maybe-tail-gen)))-	(if (pair? (cdr maybe-tail-gen))-	    (apply error "Too many arguments" unfold p f g seed maybe-tail-gen)--	    (let recur ((seed seed))-	      (if (p seed) (tail-gen seed)-		  (cons (f seed) (recur (g seed)))))))--      (let recur ((seed seed))-	(if (p seed) '()-	    (cons (f seed) (recur (g seed)))))))-      --(define (fold kons knil lis1 . lists)-  (check-arg procedure? kons fold)-  (if (pair? lists)-      (let lp ((lists (cons lis1 lists)) (ans knil))	; N-ary case-	(receive (cars+ans cdrs) (%cars+cdrs+ lists ans)-	  (if (null? cars+ans) ans ; Done.-	      (lp cdrs (apply kons cars+ans)))))-	    -      (let lp ((lis lis1) (ans knil))			; Fast path-	(if (null-list? lis) ans-	    (lp (cdr lis) (kons (car lis) ans))))))---(define (fold-right kons knil lis1 . lists)-  (check-arg procedure? kons fold-right)-  (if (pair? lists)-      (let recur ((lists (cons lis1 lists)))		; N-ary case-	(let ((cdrs (%cdrs lists)))-	  (if (null? cdrs) knil-	      (apply kons (%cars+ lists (recur cdrs))))))--      (let recur ((lis lis1))				; Fast path-	(if (null-list? lis) knil-	    (let ((head (car lis)))-	      (kons head (recur (cdr lis))))))))---(define (pair-fold-right f zero lis1 . lists)-  (check-arg procedure? f pair-fold-right)-  (if (pair? lists)-      (let recur ((lists (cons lis1 lists)))		; N-ary case-	(let ((cdrs (%cdrs lists)))-	  (if (null? cdrs) zero-	      (apply f (append! lists (list (recur cdrs)))))))--      (let recur ((lis lis1))				; Fast path-	(if (null-list? lis) zero (f lis (recur (cdr lis)))))))--(define (pair-fold f zero lis1 . lists)-  (check-arg procedure? f pair-fold)-  (if (pair? lists)-      (let lp ((lists (cons lis1 lists)) (ans zero))	; N-ary case-	(let ((tails (%cdrs lists)))-	  (if (null? tails) ans-	      (lp tails (apply f (append! lists (list ans)))))))--      (let lp ((lis lis1) (ans zero))-	(if (null-list? lis) ans-	    (let ((tail (cdr lis)))		; Grab the cdr now,-	      (lp tail (f lis ans)))))))	; in case F SET-CDR!s LIS.-      --;;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case.-;;; These cannot meaningfully be n-ary.--(define (reduce f ridentity lis)-  (check-arg procedure? f reduce)-  (if (null-list? lis) ridentity-      (fold f (car lis) (cdr lis))))--(define (reduce-right f ridentity lis)-  (check-arg procedure? f reduce-right)-  (if (null-list? lis) ridentity-      (let recur ((head (car lis)) (lis (cdr lis)))-	(if (pair? lis)-	    (f head (recur (car lis) (cdr lis)))-	    head))))----;;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;--(define (append-map f lis1 . lists)-  (really-append-map append-map  append  f lis1 lists))-(define (append-map! f lis1 . lists) -  (really-append-map append-map! append! f lis1 lists))--(define (really-append-map who appender f lis1 lists)-  (check-arg procedure? f who)-  (if (pair? lists)-      (receive (cars cdrs) (%cars+cdrs (cons lis1 lists))-	(if (null? cars) '()-	    (let recur ((cars cars) (cdrs cdrs))-	      (let ((vals (apply f cars)))-		(receive (cars2 cdrs2) (%cars+cdrs cdrs)-		  (if (null? cars2) vals-		      (appender vals (recur cars2 cdrs2))))))))--      ;; Fast path-      (if (null-list? lis1) '()-	  (let recur ((elt (car lis1)) (rest (cdr lis1)))-	    (let ((vals (f elt)))-	      (if (null-list? rest) vals-		  (appender vals (recur (car rest) (cdr rest)))))))))---(define (pair-for-each proc lis1 . lists)-  (check-arg procedure? proc pair-for-each)-  (if (pair? lists)--      (let lp ((lists (cons lis1 lists)))-	(let ((tails (%cdrs lists)))-	  (if (pair? tails)-	      (begin (apply proc lists)-		     (lp tails)))))--      ;; Fast path.-      (let lp ((lis lis1))-	(if (not (null-list? lis))-	    (let ((tail (cdr lis)))	; Grab the cdr now,-	      (proc lis)		; in case PROC SET-CDR!s LIS.-	      (lp tail))))))--;;; We stop when LIS1 runs out, not when any list runs out.-(define (map! f lis1 . lists)-  (check-arg procedure? f map!)-  (if (pair? lists)-      (let lp ((lis1 lis1) (lists lists))-	(if (not (null-list? lis1))-	    (receive (heads tails) (%cars+cdrs/no-test lists)-	      (set-car! lis1 (apply f (car lis1) heads))-	      (lp (cdr lis1) tails))))--      ;; Fast path.-      (pair-for-each (lambda (pair) (set-car! pair (f (car pair)))) lis1))-  lis1)---;;; Map F across L, and save up all the non-false results.-(define (filter-map f lis1 . lists)-  (check-arg procedure? f filter-map)-  (if (pair? lists)-      (let recur ((lists (cons lis1 lists)))-	(receive (cars cdrs) (%cars+cdrs lists)-	  (if (pair? cars)-	      (cond ((apply f cars) => (lambda (x) (cons x (recur cdrs))))-		    (else (recur cdrs))) ; Tail call in this arm.-	      '())))-	    -      ;; Fast path.-      (let recur ((lis lis1))-	(if (null-list? lis) lis-	    (let ((tail (recur (cdr lis))))-	      (cond ((f (car lis)) => (lambda (x) (cons x tail)))-		    (else tail)))))))---;;; Map F across lists, guaranteeing to go left-to-right.-;;; NOTE: Some implementations of R5RS MAP are compliant with this spec;-;;; in which case this procedure may simply be defined as a synonym for MAP.--(define (map-in-order f lis1 . lists)-  (check-arg procedure? f map-in-order)-  (if (pair? lists)-      (let recur ((lists (cons lis1 lists)))-	(receive (cars cdrs) (%cars+cdrs lists)-	  (if (pair? cars)-	      (let ((x (apply f cars)))		; Do head first,-		(cons x (recur cdrs)))		; then tail.-	      '())))-	    -      ;; Fast path.-      (let recur ((lis lis1))-	(if (null-list? lis) lis-	    (let ((tail (cdr lis))-		  (x (f (car lis))))		; Do head first,-	      (cons x (recur tail)))))))	; then tail.---;;; We extend MAP to handle arguments of unequal length.-(define map map-in-order)	---;;; filter, remove, partition-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;; FILTER, REMOVE, PARTITION and their destructive counterparts do not-;;; disorder the elements of their argument.--;; This FILTER shares the longest tail of L that has no deleted elements.-;; If Scheme had multi-continuation calls, they could be made more efficient.--(define (filter pred lis)			; Sleazing with EQ? makes this-  (check-arg procedure? pred filter)		; one faster.-  (let recur ((lis lis))		-    (if (null-list? lis) lis			; Use NOT-PAIR? to handle dotted lists.-	(let ((head (car lis))-	      (tail (cdr lis)))-	  (if (pred head)-	      (let ((new-tail (recur tail)))	; Replicate the RECUR call so-		(if (eq? tail new-tail) lis-		    (cons head new-tail)))-	      (recur tail))))))			; this one can be a tail call.---;;; Another version that shares longest tail.-;(define (filter pred lis)-;  (receive (ans no-del?)-;      ;; (recur l) returns L with (pred x) values filtered.-;      ;; It also returns a flag NO-DEL? if the returned value-;      ;; is EQ? to L, i.e. if it didn't have to delete anything.-;      (let recur ((l l))-;	(if (null-list? l) (values l #t)-;	    (let ((x  (car l))-;		  (tl (cdr l)))-;	      (if (pred x)-;		  (receive (ans no-del?) (recur tl)-;		    (if no-del?-;			(values l #t)-;			(values (cons x ans) #f)))-;		  (receive (ans no-del?) (recur tl) ; Delete X.-;		    (values ans #f))))))-;    ans))----;(define (filter! pred lis)			; Things are much simpler-;  (let recur ((lis lis))			; if you are willing to-;    (if (pair? lis)				; push N stack frames & do N-;        (cond ((pred (car lis))		; SET-CDR! writes, where N is-;               (set-cdr! lis (recur (cdr lis))); the length of the answer.-;               lis)				-;              (else (recur (cdr lis))))-;        lis)))---;;; This implementation of FILTER!-;;; - doesn't cons, and uses no stack;-;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are -;;;   usually expensive on modern machines, and can be extremely expensive on -;;;   modern Schemes (e.g., ones that have generational GC's).-;;; It just zips down contiguous runs of in and out elts in LIS doing the -;;; minimal number of SET-CDR!s to splice the tail of one run of ins to the -;;; beginning of the next.--(define (filter! pred lis)-  (check-arg procedure? pred filter!)-  (let lp ((ans lis))-    (cond ((null-list? ans)       ans)			; Scan looking for-	  ((not (pred (car ans))) (lp (cdr ans)))	; first cons of result.--	  ;; ANS is the eventual answer.-	  ;; SCAN-IN: (CDR PREV) = LIS and (CAR PREV) satisfies PRED.-	  ;;          Scan over a contiguous segment of the list that-	  ;;          satisfies PRED.-	  ;; SCAN-OUT: (CAR PREV) satisfies PRED. Scan over a contiguous-	  ;;           segment of the list that *doesn't* satisfy PRED.-	  ;;           When the segment ends, patch in a link from PREV-	  ;;           to the start of the next good segment, and jump to-	  ;;           SCAN-IN.-	  (else (letrec ((scan-in (lambda (prev lis)-				    (if (pair? lis)-					(if (pred (car lis))-					    (scan-in lis (cdr lis))-					    (scan-out prev (cdr lis))))))-			 (scan-out (lambda (prev lis)-				     (let lp ((lis lis))-				       (if (pair? lis)-					   (if (pred (car lis))-					       (begin (set-cdr! prev lis)-						      (scan-in lis (cdr lis)))-					       (lp (cdr lis)))-					   (set-cdr! prev lis))))))-		  (scan-in ans (cdr ans))-		  ans)))))----;;; Answers share common tail with LIS where possible; -;;; the technique is slightly subtle.--(define (partition pred lis)-  (check-arg procedure? pred partition)-  (let recur ((lis lis))-    (if (null-list? lis) (values lis lis)	; Use NOT-PAIR? to handle dotted lists.-	(let ((elt (car lis))-	      (tail (cdr lis)))-	  (receive (in out) (recur tail)-	    (if (pred elt)-		(values (if (pair? out) (cons elt in) lis) out)-		(values in (if (pair? in) (cons elt out) lis))))))))----;(define (partition! pred lis)			; Things are much simpler-;  (let recur ((lis lis))			; if you are willing to-;    (if (null-list? lis) (values lis lis)	; push N stack frames & do N-;        (let ((elt (car lis)))			; SET-CDR! writes, where N is-;          (receive (in out) (recur (cdr lis))	; the length of LIS.-;            (cond ((pred elt)-;                   (set-cdr! lis in)-;                   (values lis out))-;                  (else (set-cdr! lis out)-;                        (values in lis))))))))---;;; This implementation of PARTITION!-;;; - doesn't cons, and uses no stack;-;;; - is careful not to do redundant SET-CDR! writes, as writes to memory are-;;;   usually expensive on modern machines, and can be extremely expensive on -;;;   modern Schemes (e.g., ones that have generational GC's).-;;; It just zips down contiguous runs of in and out elts in LIS doing the-;;; minimal number of SET-CDR!s to splice these runs together into the result -;;; lists.--(define (partition! pred lis)-  (check-arg procedure? pred partition!)-  (if (null-list? lis) (values lis lis)--      ;; This pair of loops zips down contiguous in & out runs of the-      ;; list, splicing the runs together. The invariants are-      ;;   SCAN-IN:  (cdr in-prev)  = LIS.-      ;;   SCAN-OUT: (cdr out-prev) = LIS.-      (letrec ((scan-in (lambda (in-prev out-prev lis)-			  (let lp ((in-prev in-prev) (lis lis))-			    (if (pair? lis)-				(if (pred (car lis))-				    (lp lis (cdr lis))-				    (begin (set-cdr! out-prev lis)-					   (scan-out in-prev lis (cdr lis))))-				(set-cdr! out-prev lis))))) ; Done.--	       (scan-out (lambda (in-prev out-prev lis)-			   (let lp ((out-prev out-prev) (lis lis))-			     (if (pair? lis)-				 (if (pred (car lis))-				     (begin (set-cdr! in-prev lis)-					    (scan-in lis out-prev (cdr lis)))-				     (lp lis (cdr lis)))-				 (set-cdr! in-prev lis)))))) ; Done.--	;; Crank up the scan&splice loops.-	(if (pred (car lis))-	    ;; LIS begins in-list. Search for out-list's first pair.-	    (let lp ((prev-l lis) (l (cdr lis)))-	      (cond ((not (pair? l)) (values lis l))-		    ((pred (car l)) (lp l (cdr l)))-		    (else (scan-out prev-l l (cdr l))-			  (values lis l))))	; Done.--	    ;; LIS begins out-list. Search for in-list's first pair.-	    (let lp ((prev-l lis) (l (cdr lis)))-	      (cond ((not (pair? l)) (values l lis))-		    ((pred (car l))-		     (scan-in l prev-l (cdr l))-		     (values l lis))		; Done.-		    (else (lp l (cdr l)))))))))---;;; Inline us, please.-(define (remove  pred l) (filter  (lambda (x) (not (pred x))) l))-(define (remove! pred l) (filter! (lambda (x) (not (pred x))) l))----;;; Here's the taxonomy for the DELETE/ASSOC/MEMBER functions.-;;; (I don't actually think these are the world's most important-;;; functions -- the procedural FILTER/REMOVE/FIND/FIND-TAIL variants-;;; are far more general.)-;;;-;;; Function			Action-;;; ----------------------------------------------------------------------------;;; remove pred lis		Delete by general predicate-;;; delete x lis [=]		Delete by element comparison-;;;					     -;;; find pred lis		Search by general predicate-;;; find-tail pred lis		Search by general predicate-;;; member x lis [=]		Search by element comparison-;;;-;;; assoc key lis [=]		Search alist by key comparison-;;; alist-delete key alist [=]	Alist-delete by key comparison--(define (delete x lis . maybe-=) -  (let ((= (:optional maybe-= equal?)))-    (filter (lambda (y) (not (= x y))) lis)))--(define (delete! x lis . maybe-=)-  (let ((= (:optional maybe-= equal?)))-    (filter! (lambda (y) (not (= x y))) lis)))--;;; Extended from R4RS to take an optional comparison argument.-(define (member x lis . maybe-=)-  (let ((= (:optional maybe-= equal?)))-    (find-tail (lambda (y) (= x y)) lis)))--;;; R4RS, hence we don't bother to define.-;;; The MEMBER and then FIND-TAIL call should definitely-;;; be inlined for MEMQ & MEMV.-;(define (memq    x lis) (member x lis eq?))-;(define (memv    x lis) (member x lis eqv?))---;;; right-duplicate deletion-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;; delete-duplicates delete-duplicates!-;;;-;;; Beware -- these are N^2 algorithms. To efficiently remove duplicates-;;; in long lists, sort the list to bring duplicates together, then use a -;;; linear-time algorithm to kill the dups. Or use an algorithm based on-;;; element-marking. The former gives you O(n lg n), the latter is linear.--(define (delete-duplicates lis . maybe-=)-  (let ((elt= (:optional maybe-= equal?)))-    (check-arg procedure? elt= delete-duplicates)-    (let recur ((lis lis))-      (if (null-list? lis) lis-	  (let* ((x (car lis))-		 (tail (cdr lis))-		 (new-tail (recur (delete x tail elt=))))-	    (if (eq? tail new-tail) lis (cons x new-tail)))))))--(define (delete-duplicates! lis maybe-=)-  (let ((elt= (:optional maybe-= equal?)))-    (check-arg procedure? elt= delete-duplicates!)-    (let recur ((lis lis))-      (if (null-list? lis) lis-	  (let* ((x (car lis))-		 (tail (cdr lis))-		 (new-tail (recur (delete! x tail elt=))))-	    (if (eq? tail new-tail) lis (cons x new-tail)))))))---;;; alist stuff-;;;;;;;;;;;;;;;--;;; Extended from R4RS to take an optional comparison argument.-(define (assoc x lis . maybe-=)-  (let ((= (:optional maybe-= equal?)))-    (find (lambda (entry) (= x (car entry))) lis)))--(define (alist-cons key datum alist) (cons (cons key datum) alist))--(define (alist-copy alist)-  (map (lambda (elt) (cons (car elt) (cdr elt)))-       alist))--(define (alist-delete key alist . maybe-=)-  (let ((= (:optional maybe-= equal?)))-    (filter (lambda (elt) (not (= key (car elt)))) alist)))--(define (alist-delete! key alist . maybe-=)-  (let ((= (:optional maybe-= equal?)))-    (filter! (lambda (elt) (not (= key (car elt)))) alist)))---;;; find find-tail take-while drop-while span break any every list-index-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;--(define (find pred list)-  (cond ((find-tail pred list) => car)-	(else #f)))--(define (find-tail pred list)-  (check-arg procedure? pred find-tail)-  (let lp ((list list))-    (and (not (null-list? list))-	 (if (pred (car list)) list-	     (lp (cdr list))))))--(define (take-while pred lis)-  (check-arg procedure? pred take-while)-  (let recur ((lis lis))-    (if (null-list? lis) '()-	(let ((x (car lis)))-	  (if (pred x)-	      (cons x (recur (cdr lis)))-	      '())))))--(define (drop-while pred lis)-  (check-arg procedure? pred drop-while)-  (let lp ((lis lis))-    (if (null-list? lis) '()-	(if (pred (car lis))-	    (lp (cdr lis))-	    lis))))--(define (take-while! pred lis)-  (check-arg procedure? pred take-while!)-  (if (or (null-list? lis) (not (pred (car lis)))) '()-      (begin (let lp ((prev lis) (rest (cdr lis)))-	       (if (pair? rest)-		   (let ((x (car rest)))-		     (if (pred x) (lp rest (cdr rest))-			 (set-cdr! prev '())))))-	     lis)))--(define (span pred lis)-  (check-arg procedure? pred span)-  (let recur ((lis lis))-    (if (null-list? lis) (values '() '())-	(let ((x (car lis)))-	  (if (pred x)-	      (receive (prefix suffix) (recur (cdr lis))-		(values (cons x prefix) suffix))-	      (values '() lis))))))--(define (span! pred lis)-  (check-arg procedure? pred span!)-  (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis)-      (let ((suffix (let lp ((prev lis) (rest (cdr lis)))-		      (if (null-list? rest) rest-			  (let ((x (car rest)))-			    (if (pred x) (lp rest (cdr rest))-				(begin (set-cdr! prev '())-				       rest)))))))-	(values lis suffix))))-  --(define (break  pred lis) (span  (lambda (x) (not (pred x))) lis))-(define (break! pred lis) (span! (lambda (x) (not (pred x))) lis))--(define (any pred lis1 . lists)-  (check-arg procedure? pred any)-  (if (pair? lists)--      ;; N-ary case-      (receive (heads tails) (%cars+cdrs (cons lis1 lists))-	(and (pair? heads)-	     (let lp ((heads heads) (tails tails))-	       (receive (next-heads next-tails) (%cars+cdrs tails)-		 (if (pair? next-heads)-		     (or (apply pred heads) (lp next-heads next-tails))-		     (apply pred heads)))))) ; Last PRED app is tail call.--      ;; Fast path-      (and (not (null-list? lis1))-	   (let lp ((head (car lis1)) (tail (cdr lis1)))-	     (if (null-list? tail)-		 (pred head)		; Last PRED app is tail call.-		 (or (pred head) (lp (car tail) (cdr tail))))))))---;(define (every pred list)              ; Simple definition.-;  (let lp ((list list))                ; Doesn't return the last PRED value.-;    (or (not (pair? list))-;        (and (pred (car list))-;             (lp (cdr list))))))--(define (every pred lis1 . lists)-  (check-arg procedure? pred every)-  (if (pair? lists)--      ;; N-ary case-      (receive (heads tails) (%cars+cdrs (cons lis1 lists))-	(or (not (pair? heads))-	    (let lp ((heads heads) (tails tails))-	      (receive (next-heads next-tails) (%cars+cdrs tails)-		(if (pair? next-heads)-		    (and (apply pred heads) (lp next-heads next-tails))-		    (apply pred heads)))))) ; Last PRED app is tail call.--      ;; Fast path-      (or (null-list? lis1)-	  (let lp ((head (car lis1))  (tail (cdr lis1)))-	    (if (null-list? tail)-		(pred head)	; Last PRED app is tail call.-		(and (pred head) (lp (car tail) (cdr tail))))))))--(define (list-index pred lis1 . lists)-  (check-arg procedure? pred list-index)-  (if (pair? lists)--      ;; N-ary case-      (let lp ((lists (cons lis1 lists)) (n 0))-	(receive (heads tails) (%cars+cdrs lists)-	  (and (pair? heads)-	       (if (apply pred heads) n-		   (lp tails (+ n 1))))))--      ;; Fast path-      (let lp ((lis lis1) (n 0))-	(and (not (null-list? lis))-	     (if (pred (car lis)) n (lp (cdr lis) (+ n 1)))))))--;;; Reverse-;;;;;;;;;;;--;R4RS, so not defined here.-;(define (reverse lis) (fold cons '() lis))-				      -;(define (reverse! lis)-;  (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() lis))--(define (reverse! lis)-  (let lp ((lis lis) (ans '()))-    (if (null-list? lis) ans-        (let ((tail (cdr lis)))-          (set-cdr! lis ans)-          (lp tail lis)))))--;;; Lists-as-sets-;;;;;;;;;;;;;;;;;--;;; This is carefully tuned code; do not modify casually.-;;; - It is careful to share storage when possible;-;;; - Side-effecting code tries not to perform redundant writes.-;;; - It tries to avoid linear-time scans in special cases where constant-time-;;;   computations can be performed.-;;; - It relies on similar properties from the other list-lib procs it calls.-;;;   For example, it uses the fact that the implementations of MEMBER and-;;;   FILTER in this source code share longest common tails between args-;;;   and results to get structure sharing in the lset procedures.--(define (%lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1))--(define (lset<= = . lists)-  (check-arg procedure? = lset<=)-  (or (not (pair? lists)) ; 0-ary case-      (let lp ((s1 (car lists)) (rest (cdr lists)))-	(or (not (pair? rest))-	    (let ((s2 (car rest))  (rest (cdr rest)))-	      (and (or (eq? s2 s1)	; Fast path-		       (%lset2<= = s1 s2)) ; Real test-		   (lp s2 rest)))))))--(define (lset= = . lists)-  (check-arg procedure? = lset=)-  (or (not (pair? lists)) ; 0-ary case-      (let lp ((s1 (car lists)) (rest (cdr lists)))-	(or (not (pair? rest))-	    (let ((s2   (car rest))-		  (rest (cdr rest)))-	      (and (or (eq? s1 s2)	; Fast path-		       (and (%lset2<= = s1 s2) (%lset2<= = s2 s1))) ; Real test-		   (lp s2 rest)))))))---(define (lset-adjoin = lis . elts)-  (check-arg procedure? = lset-adjoin)-  (fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans)))-	lis elts))---(define (lset-union = . lists)-  (check-arg procedure? = lset-union)-  (reduce (lambda (lis ans)		; Compute ANS + LIS.-	    (cond ((null? lis) ans)	; Don't copy any lists-		  ((null? ans) lis) 	; if we don't have to.-		  ((eq? lis ans) ans)-		  (else-		   (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans)-					       ans-					       (cons elt ans)))-			 ans lis))))-	  '() lists))--(define (lset-union! = . lists)-  (check-arg procedure? = lset-union!)-  (reduce (lambda (lis ans)		; Splice new elts of LIS onto the front of ANS.-	    (cond ((null? lis) ans)	; Don't copy any lists-		  ((null? ans) lis) 	; if we don't have to.-		  ((eq? lis ans) ans)-		  (else-		   (pair-fold (lambda (pair ans)-				(let ((elt (car pair)))-				  (if (any (lambda (x) (= x elt)) ans)-				      ans-				      (begin (set-cdr! pair ans) pair))))-			      ans lis))))-	  '() lists))---(define (lset-intersection = lis1 . lists)-  (check-arg procedure? = lset-intersection)-  (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.-    (cond ((any null-list? lists) '())		; Short cut-	  ((null? lists)          lis1)		; Short cut-	  (else (filter (lambda (x)-			  (every (lambda (lis) (member x lis =)) lists))-			lis1)))))--(define (lset-intersection! = lis1 . lists)-  (check-arg procedure? = lset-intersection!)-  (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.-    (cond ((any null-list? lists) '())		; Short cut-	  ((null? lists)          lis1)		; Short cut-	  (else (filter! (lambda (x)-			   (every (lambda (lis) (member x lis =)) lists))-			 lis1)))))---(define (lset-difference = lis1 . lists)-  (check-arg procedure? = lset-difference)-  (let ((lists (filter pair? lists)))	; Throw out empty lists.-    (cond ((null? lists)     lis1)	; Short cut-	  ((memq lis1 lists) '())	; Short cut-	  (else (filter (lambda (x)-			  (every (lambda (lis) (not (member x lis =)))-				 lists))-			lis1)))))--(define (lset-difference! = lis1 . lists)-  (check-arg procedure? = lset-difference!)-  (let ((lists (filter pair? lists)))	; Throw out empty lists.-    (cond ((null? lists)     lis1)	; Short cut-	  ((memq lis1 lists) '())	; Short cut-	  (else (filter! (lambda (x)-			   (every (lambda (lis) (not (member x lis =)))-				  lists))-			 lis1)))))---(define (lset-xor = . lists)-  (check-arg procedure? = lset-xor)-  (reduce (lambda (b a)			; Compute A xor B:-	    ;; Note that this code relies on the constant-time-	    ;; short-cuts provided by LSET-DIFF+INTERSECTION,-	    ;; LSET-DIFFERENCE & APPEND to provide constant-time short-	    ;; cuts for the cases A = (), B = (), and A eq? B. It takes-	    ;; a careful case analysis to see it, but it's carefully-	    ;; built in.--	    ;; Compute a-b and a^b, then compute b-(a^b) and-	    ;; cons it onto the front of a-b.-	    (receive (a-b a-int-b)   (lset-diff+intersection = a b)-	      (cond ((null? a-b)     (lset-difference = b a))-		    ((null? a-int-b) (append b a))-		    (else (fold (lambda (xb ans)-				  (if (member xb a-int-b =) ans (cons xb ans)))-				a-b-				b)))))-	  '() lists))---(define (lset-xor! = . lists)-  (check-arg procedure? = lset-xor!)-  (reduce (lambda (b a)			; Compute A xor B:-	    ;; Note that this code relies on the constant-time-	    ;; short-cuts provided by LSET-DIFF+INTERSECTION,-	    ;; LSET-DIFFERENCE & APPEND to provide constant-time short-	    ;; cuts for the cases A = (), B = (), and A eq? B. It takes-	    ;; a careful case analysis to see it, but it's carefully-	    ;; built in.--	    ;; Compute a-b and a^b, then compute b-(a^b) and-	    ;; cons it onto the front of a-b.-	    (receive (a-b a-int-b)   (lset-diff+intersection! = a b)-	      (cond ((null? a-b)     (lset-difference! = b a))-		    ((null? a-int-b) (append! b a))-		    (else (pair-fold (lambda (b-pair ans)-				       (if (member (car b-pair) a-int-b =) ans-					   (begin (set-cdr! b-pair ans) b-pair)))-				     a-b-				     b)))))-	  '() lists))---(define (lset-diff+intersection = lis1 . lists)-  (check-arg procedure? = lset-diff+intersection)-  (cond ((every null-list? lists) (values lis1 '()))	; Short cut-	((memq lis1 lists)        (values '() lis1))	; Short cut-	(else (partition (lambda (elt)-			   (not (any (lambda (lis) (member elt lis =))-				     lists)))-			 lis1))))--(define (lset-diff+intersection! = lis1 . lists)-  (check-arg procedure? = lset-diff+intersection!)-  (cond ((every null-list? lists) (values lis1 '()))	; Short cut-	((memq lis1 lists)        (values '() lis1))	; Short cut-	(else (partition! (lambda (elt)-			    (not (any (lambda (lis) (member elt lis =))-				      lists)))-			  lis1))))
− srfi/srfi-55.scm
@@ -1,53 +0,0 @@-;;;-;;; husk-scheme-;;; http://justinethier.github.com/husk-scheme-;;;-;;; Reference implementation for SRFI-55-;;; from http://srfi.schemers.org/srfi-55/srfi-55.html-;;;-;;; Requirements: SRFI-23 (error reporting)-;;;--; -; Example of registering extensions:-;    (register-extension '(srfi 1) "srfi/srfi-1.scm")-; Example of loading an extension:-;   (require-extension (srfi 1))-;---(define available-extensions '())--(define (register-extension id action . compare)-  (set! available-extensions-    (cons (list (if (pair? compare) (car compare) equal?)-		id -		action)-	  available-extensions)) )--(define (find-extension id)-  (define (lookup exts)-    (if (null? exts)-	  (write (list "extension not found - please contact your vendor " id))-	  (let ((ext (car exts)))-	    (if ((car ext) (cadr ext) id)-	        (caddr ext) ; Return a string instead of calling a function ((caddr ext))-	        (lookup (cdr exts)) ) ) ) )-  (lookup available-extensions) )--(define-syntax require-extension -  (syntax-rules (srfi)-; A temporary version that works for one srfi, but needs to be-; generalized. The issue is that introducing a new scope will-; cause the load to be ineffective because the outer scope will-; not be affected-    ((_ (srfi id ...))-     (load (find-extension '(srfi id) ...)) ) ; TODO: Maybe we could have a load-all that accepts a list?-    ))-;    ((_ "internal" (srfi id ...))-;     (begin (find-extension '(srfi id) ...)) )-;    ((_ "internal" id)-;     (find-extension 'id) )-;    ((_ clause ...)-;     (begin (require-extension "internal" clause) ...)) ) )-
− stdlib.scm
@@ -1,453 +0,0 @@-;;;-;;; husk-scheme-;;; http://justinethier.github.com/husk-scheme-;;;-;;; Written by Justin Ethier-;;;-;;; Standard library of scheme functions-;;;-(define call/cc call-with-current-continuation)--(define (caar pair) (car (car pair)))-(define (cadr pair) (car (cdr pair)))-(define (cdar pair) (cdr (car pair)))-(define (cddr pair) (cdr (cdr pair)))-(define (caaar pair) (car (car (car pair))))-(define (caadr pair) (car (car (cdr pair))))-(define (cadar pair) (car (cdr (car pair))))-(define (caddr pair) (car (cdr (cdr pair))))-(define (cdaar pair) (cdr (car (car pair))))-(define (cdadr pair) (cdr (car (cdr pair))))-(define (cddar pair) (cdr (cdr (car pair))))-(define (cdddr pair) (cdr (cdr (cdr pair))))-(define (caaaar pair) (car (car (car (car pair)))))-(define (caaadr pair) (car (car (car (cdr pair)))))-(define (caadar pair) (car (car (cdr (car pair)))))-(define (caaddr pair) (car (car (cdr (cdr pair)))))-(define (cadaar pair) (car (cdr (car (car pair)))))-(define (cadadr pair) (car (cdr (car (cdr pair)))))-(define (caddar pair) (car (cdr (cdr (car pair)))))-(define (cadddr pair) (car (cdr (cdr (cdr pair)))))-(define (cdaaar pair) (cdr (car (car (car pair)))))-(define (cdaadr pair) (cdr (car (car (cdr pair)))))-(define (cdadar pair) (cdr (car (cdr (car pair)))))-(define (cdaddr pair) (cdr (car (cdr (cdr pair)))))-(define (cddaar pair) (cdr (cdr (car (car pair)))))-(define (cddadr pair) (cdr (cdr (car (cdr pair)))))-(define (cdddar pair) (cdr (cdr (cdr (car pair)))))-(define (cddddr pair) (cdr (cdr (cdr (cdr pair)))))---(define (not x)      (if x #f #t))--(define (list . objs)  objs)-(define (id obj)       obj)--; TODO: this is not flipping args. not part of R5RS, but -;       as it is now, what is the point?-(define (flip func)    (lambda (arg1 arg2) (func arg1 arg2)))--(define (curry func arg1)  (lambda (arg) (apply func (cons arg1 (list arg)))))-(define (compose f g)      (lambda (arg) (f (apply g arg))))--(define (foldr func end lst)-  (if (null? lst)-	  end-	  (func (car lst) (foldr func end (cdr lst)))))--(define (foldl func accum lst)-  (if (null? lst)-	  accum-	  (foldl func (func (car lst) accum) (cdr lst))))--(define (sum . lst)     (foldl + 0 lst))-(define (product . lst) (foldl * 1 lst))--; Forms from R5RS for and/or-(define-syntax and-  (syntax-rules ()-    ((and) #t)-    ((and test) test)-    ((and test1 test2 ...)-     (if test1 (and test2 ...) #f))))--(define-syntax or-  (syntax-rules ()-    ((or) #f)-    ((or test) test)-    ((or test1 test2 ...)-     (let ((x test1))-       (if x x (or test2 ...))))))--(define (abs num)-  (if (negative? num)-      (* num -1)-      num))--(define (max first . rest) (foldl (lambda (old new) (if (> old new) old new)) first rest))-(define (min first . rest) (foldl (lambda (old new) (if (< old new) old new)) first rest))--(define zero?        (curry = 0))-(define positive?    (curry < 0))-(define negative?    (curry > 0))-(define (odd? num)   (= (modulo num 2) 1))-(define (even? num)  (= (modulo num 2) 0))--(define (length lst)    (foldl (lambda (x y) (+ y 1)) 0 lst))-(define (reverse lst)   (foldl (flip cons) '() lst))--(define-syntax begin-  (syntax-rules ()-    ((begin exp ...)-      ((lambda () exp ...)))))--; cond-; Form from R5RS:-(define-syntax cond-  (syntax-rules (else =>)-    ((cond (else result1 result2 ...))-;-; TODO: see pitfall 3.2-;-; This is a modification from R5RS - we put the begin within-; an if statement, because in this context definitions are-; not allowed. This prevents one from interfering with macro-; hygiene. -;-; TODO: unfortunately the macro logic has not yet been-; updated to take this into acccount, so the pitfall-; still fails-;-     (if #t (begin result1 result2 ...)))-    ((cond (test => result))-     (let ((temp test))-       (if temp (result temp))))-    ((cond (test => result) clause1 clause2 ...)-     (let ((temp test))-       (if temp-           (result temp)-           (cond clause1 clause2 ...))))-    ((cond (test)) test)-    ((cond (test) clause1 clause2 ...)-     (let ((temp test))-       (if temp-           temp-           (cond clause1 clause2 ...))))-    ((cond (test result1 result2 ...))-     (if test (begin result1 result2 ...)))-    ((cond (test result1 result2 ...)-           clause1 clause2 ...)-     (if test-         (begin result1 result2 ...)-         (cond clause1 clause2 ...)))))-; Case-; Form from R5RS:-(define-syntax case-  (syntax-rules (else)-    ((case (key ...)-       clauses ...)-     (let ((atom-key (key ...)))-       (case atom-key clauses ...)))-    ((case key-       (else result1 result2 ...))-     (if #t (begin result1 result2 ...)))-    ((case key-       ((atoms ...) result1 result2 ...))-     (if (memv key '(atoms ...))-         (begin result1 result2 ...)))-    ((case key-       ((atoms ...) result1 result2 ...)-       clause clauses ...)-     (if (memv key '(atoms ...))-         (begin result1 result2 ...)-         (case key clause clauses ...)))))--(define (my-mem-helper obj lst cmp-proc)- (cond -   ((null? lst) #f)-   ((cmp-proc obj (car lst)) lst)-   (else (my-mem-helper obj (cdr lst) cmp-proc))))-(define (memq obj lst) (my-mem-helper obj lst eq?))-(define (memv obj lst) (my-mem-helper obj lst eqv?))-(define (member obj lst) (my-mem-helper obj lst equal?))--(define (mem-helper pred op)  (lambda (next acc) (if (and (not acc) (pred (op next))) next acc)))-(define (assq obj alist)      (foldl (mem-helper (curry eq? obj) car) #f alist))-(define (assv obj alist)      (foldl (mem-helper (curry eqv? obj) car) #f alist))-(define (assoc obj alist)     (foldl (mem-helper (curry equal? obj) car) #f alist))--; FUTURE: on map and for-each - Support variable number of args, per spec:-; http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_sec_6.4-;(define (for-each func . lsts) )--(define (for-each func lst) -  (if (eq? 1 (length lst))-	(func (car lst))-    (begin (func (car lst))-           (for-each func (cdr lst)))))--(define (map func lst)        (foldr (lambda (x y) (cons (func x) y)) '() lst))--(define (list-tail lst k) -        (if (zero? k)-          lst-          (list-tail (cdr lst) (- k 1))))-(define (list-ref lst k)  (car (list-tail lst k)))--; append accepts a variable number of arguments, per R5RS. So a wrapper-; has been provided for the standard 2-argument version of (append).-;-; We return the given value if less than 2 arguments are given, and-; otherwise fold over each arg, appending it to its predecessor. -(define (append . lst)-  (let ((append-2 -          (lambda (inlist alist) -                  (foldr (lambda (ap in) (cons ap in)) alist inlist))))-    (if (null? lst)-        lst-        (if (null? (cdr lst))-            (car lst)-            (foldl (lambda (a b) (append-2 b a)) (car lst) (cdr lst))))))--; Let forms-;-; letrec from R5RS-(define-syntax letrec-    (syntax-rules ()-      ((letrec ((var1 init1) ...) body ...)-       (letrec "generate_temp_names"-         (var1 ...)-         ()-         ((var1 init1) ...)-         body ...))-      ((letrec "generate_temp_names"-         ()-         (temp1 ...)-         ((var1 init1) ...)-         body ...)                         ; start the changed code-       (let ((var1 #f) ...)-         (let ((temp1 init1) ...)-           (set! var1 temp1)-           ...-           body ...)))-      ((letrec "generate_temp_names"-         (x y ...)-         (temp ...)-         ((var1 init1) ...)-         body ...)-       (letrec "generate_temp_names"-         (y ...)-         (newtemp temp ...)-         ((var1 init1) ...)-         body ...))))--; let and named let (using the Y-combinator):-(define-syntax let-  (syntax-rules ()-    ((_ ((x v) ...) e1 e2 ...)-     ((lambda (x ...) e1 e2 ...) v ...))-    ((_ name ((x v) ...) e1 e2 ...)-     (let*-       ((f  (lambda (name)-              (lambda (x ...) e1 e2 ...)))-        (ff ((lambda (proc) (f (lambda (x ...) ((proc proc)-               x ...))))-             (lambda (proc) (f (lambda (x ...) ((proc proc)-               x ...)))))))-        (ff v ...)))))--;-; It would be nice to change first rule back to:-;    ((_ () body) body)-;-(define-syntax let*-  (syntax-rules ()-    ((let* () body1 body2 ...)-     (let () body1 body2 ...))-    ((let* ((name1 val1) (name2 val2) ...)-       body1 body2 ...)-     (let ((name1 val1))-       (let* ((name2 val2) ...)-         body1 body2 ...)))))--; Iteration - do-(define-syntax do-  (syntax-rules ()-    ((do ((var init step ...) ...)-         (test expr ...)-         command ...)-     (letrec-       ((loop-         (lambda (var ...)-           (if test-               (begin-                 (if #f #f)-                 expr ...)-               (begin-                 command-                 ...-                 (loop (do "step" var step ...)-                       ...))))))-       (loop init ...)))-    ((do "step" x)-     x)-    ((do "step" x y)-     y)))--; Delayed evaluation functions-(define force-    (lambda (object)-	      (object)))--(define-syntax delay -  (syntax-rules () -    ((delay expression)-     (make-promise (lambda () expression)))))--(define make-promise-  (lambda (proc)-    (let ((result-ready? #f)-          (result #f))-      (lambda ()-        (if result-ready? -            result-            (let ((x (proc)))-              (if result-ready?-                  result-                  (begin (set! result x)-                         (set! result-ready? #t)-                         result))))))))--; End delayed evaluation section--; String Section-(define-syntax string-fill!-  (syntax-rules ()-    ((_ _str _chr)-     (set! _str-           (make-string (string-length _str) _chr)))))--; Vector Section-(define-syntax vector-fill!-  (syntax-rules ()-    ((_ _vec _fill)-     (set! _vec-           (make-vector (vector-length _vec) _fill)))))--; Continuation Section-(define (values . things)-    (call-with-current-continuation -        (lambda (cont) (apply cont things))))--;; I/O Section-(define (newline . port)-  (if (null? port) -      (display #\newline) -      (display #\newline port)))--; TODO: test these forms-(define (call-with-input-file filename proc)-  (let ((opened-file (open-input-file filename)))-    (define result-           (proc opened-file))-    (close-input-port opened-file)-    result))-; TODO: test-(define (call-with-output-file filename proc)-  (let ((opened-file (open-output-file filename)))-    (define result-           (proc opened-file))-    (close-output-port opened-file)-    result))--;; SRFI 23 - Error reporting mechanism-;; based on code from: http://srfi.schemers.org/srfi-23/srfi-23.html-(define (error reason . args)-    (display "Error: ")-    (display reason)-    (newline)-    (for-each (lambda (arg) -                (display " ")-      	  (write arg))-      	args)-    (newline)-    (exit-fail))--;; Hashtable derived forms-(define hash-table-walk-  (lambda (ht proc)-    (map -      (lambda (kv) (proc (car kv) (car (reverse kv))))-      (hash-table->alist ht)))) --(define (hash-table-update! hash-table key function)-  (hash-table-set! hash-table key-                  (function (hash-table-ref hash-table key thunk))))--(define-syntax hash-table-merge!-  (syntax-rules ()-    ((_ hdest hsrc)-     (map (lambda (node) (hash-table-set! hdest -                                       (car node)-                                       (cadr node)))-       (hash-table->alist hsrc)))))--(define (alist->hash-table lst)- (let ((ht (make-hash-table)))-   (for-each (lambda (node)-              (hash-table-set! ht (car node) (cadr node)))-             lst)-   ht))--(define (hash-table-fold hash-table f acc-in)-  (let ((acc acc-in))-    (hash-table-walk hash-table -             (lambda (key value) (set! acc (f key value acc))))-      acc))--; Implementations of gcd and lcm using Euclid's algorithm-;-; Also note that each form is written to accept either 0 or-; 2 arguments, per R5RS. This could probably be generalized-; even further, if necessary.-;-(define gcd '())-(define lcm '())--(let ()-  ; Main GCD algorithm-  (define (gcd/main a b)-    (if (= b 0)-      (abs a)-      (gcd/main b (modulo a b))))--  ; A helper function to reduce the input list-  (define (gcd/entry . nums)-    (if (eqv? nums '())-      0-      (foldl gcd/main (car nums) (cdr nums))))--  ; Main LCM algorithm-  (define (lcm/main a b)-    (abs (/ (* a b) (gcd/main a b))))--  ; A helper function to reduce the input list-  (define (lcm/entry . nums)-    (if (eqv? nums '())-      1-      (foldl lcm/main (car nums) (cdr nums))))--  (set! gcd gcd/entry)-  (set! lcm lcm/entry))  --; SRFI 8-; Reference implementation from: http://srfi.schemers.org/srfi-8/srfi-8.html-;-; FUTURE: This may be moved into its own file-;-(define-syntax receive-    (syntax-rules ()-        ((receive formals expression body ...)-         (call-with-values (lambda () expression)-             (lambda formals body ...)))))-