* eval.h (SCM_EVALIM2): New macro. Use it when a
[bpt/guile.git] / libguile / eval.c
index b7cf5be..549d7b4 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1995, 96, 97, 98, 99, 2000, 2001 Free Software Foundation, Inc.
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -448,6 +448,7 @@ const char scm_s_bindings[] = "bad bindings";
 const char scm_s_variable[] = "bad variable";
 const char scm_s_clauses[] = "bad or missing clauses";
 const char scm_s_formals[] = "bad formals";
+const char scm_s_duplicate_formals[] = "duplicate formals";
 
 SCM_GLOBAL_SYMBOL (scm_sym_dot, ".");
 SCM_GLOBAL_SYMBOL (scm_sym_arrow, "=>");
@@ -595,7 +596,8 @@ scm_m_case (SCM xorig, SCM env)
       proc = SCM_CAR (x);
       SCM_ASSYNT (scm_ilength (proc) >= 2, xorig, scm_s_clauses, s_case);
       SCM_ASSYNT (scm_ilength (SCM_CAR (proc)) >= 0
-                 || SCM_EQ_P (scm_sym_else, SCM_CAR (proc)),
+                 || (SCM_EQ_P (scm_sym_else, SCM_CAR (proc)) 
+                     && SCM_NULLP (SCM_CDR (x))),
                  xorig, scm_s_clauses, s_case);
     }
   return scm_cons (SCM_IM_CASE, cdrx);
@@ -634,6 +636,21 @@ scm_m_cond (SCM xorig, SCM env)
 SCM_SYNTAX(s_lambda, "lambda", scm_makmmacro, scm_m_lambda);
 SCM_GLOBAL_SYMBOL(scm_sym_lambda, s_lambda);
 
+/* Return #t if OBJ is `eq?' to one of the elements of LIST or to the
+   cdr of the last cons.  (Thus, LIST is not required to be a proper
+   list and when OBJ also found in the improper ending.) */
+
+static int
+scm_c_improper_memq (SCM obj, SCM list)
+{
+  for (; SCM_CONSP (list); list = SCM_CDR (list))
+    {
+      if (SCM_EQ_P (SCM_CAR (list), obj))
+       return SCM_BOOL_T;
+    }
+  return SCM_EQ_P (list, obj);
+}
+
 SCM 
 scm_m_lambda (SCM xorig, SCM env)
 {
@@ -662,6 +679,8 @@ scm_m_lambda (SCM xorig, SCM env)
        }
       if (!SCM_SYMBOLP (SCM_CAR (proc)))
        goto badforms;
+      else if (scm_c_improper_memq (SCM_CAR(proc), SCM_CDR(proc)))
+       scm_wta (xorig, scm_s_duplicate_formals, s_lambda);
       proc = SCM_CDR (proc);
     }
   if (SCM_NNULLP (proc))
@@ -1671,7 +1690,9 @@ scm_option scm_evaluator_trap_table[] = {
 
 SCM_DEFINE (scm_eval_options_interface, "eval-options-interface", 0, 1, 0, 
             (SCM setting),
-           "")
+           "Option interface for the evaluation options. Instead of using\n"
+           "this procedure directly, use the procedures @code{eval-enable},\n"
+           "@code{eval-disable}, @code{eval-set!} and @var{eval-options}.")
 #define FUNC_NAME s_scm_eval_options_interface
 {
   SCM ans;
@@ -1688,7 +1709,7 @@ SCM_DEFINE (scm_eval_options_interface, "eval-options-interface", 0, 1, 0,
 
 SCM_DEFINE (scm_evaluator_traps, "evaluator-traps-interface", 0, 1, 0, 
             (SCM setting),
-           "")
+           "Option interface for the evaluator trap options.")
 #define FUNC_NAME s_scm_evaluator_traps
 {
   SCM ans;
@@ -1903,6 +1924,36 @@ dispatch:
       x = SCM_CDR (x);
 
     begin:
+      /* If we are on toplevel with a lookup closure, we need to sync
+         with the current module. */
+      if (SCM_CONSP(env) && !SCM_CONSP(SCM_CAR(env)))
+       {
+         t.arg1 = x;
+         {
+           SCM p = scm_current_module_lookup_closure ();
+           if (p != SCM_CAR(env))
+             env = scm_top_level_env (p);
+         }
+         while (SCM_NNULLP (t.arg1 = SCM_CDR (t.arg1)))
+           {
+             EVALCAR (x, env);
+             x = t.arg1;
+             {
+               SCM p = scm_current_module_lookup_closure ();
+               if (p != SCM_CAR(env))
+                 env = scm_top_level_env (p);
+             }
+           }
+         goto carloop;
+       }
+      else
+       goto nontoplevel_begin;
+
+    nontoplevel_cdrxnoap:
+      PREP_APPLY (SCM_UNDEFINED, SCM_EOL);
+    nontoplevel_cdrxbegin:
+      x = SCM_CDR (x);
+    nontoplevel_begin:
       t.arg1 = x;
       while (SCM_NNULLP (t.arg1 = SCM_CDR (t.arg1)))
        {
@@ -1911,14 +1962,16 @@ dispatch:
              if (SCM_ISYMP (SCM_CAR (x)))
                {
                  x = scm_m_expand_body (x, env);
-                 goto begin;
+                 goto nontoplevel_begin;
                }
+             else
+               SCM_EVALIM2 (SCM_CAR(x));
            }
          else
            SCM_CEVAL (SCM_CAR (x), env);
          x = t.arg1;
        }
-
+      
     carloop:                   /* scm_eval car of last form in list */
       if (SCM_NCELLP (SCM_CAR (x)))
        {
@@ -1985,6 +2038,8 @@ dispatch:
              SCM_ASRTGO (SCM_NIMP (proc), badfun);
              PREP_APPLY (proc, scm_cons (t.arg1, SCM_EOL));
              ENTER_APPLY;
+             if (SCM_CLOSUREP(proc) && scm_badformalsp (proc, 1))
+               goto umwrongnumargs;
              goto evap1;
            }
        }
@@ -2019,7 +2074,7 @@ dispatch:
       if (SCM_NULLP (x))
        RETURN (SCM_UNSPECIFIED);
       PREP_APPLY (SCM_UNDEFINED, SCM_EOL);
-      goto begin;
+      goto nontoplevel_begin;
 
 
     case SCM_BIT8(SCM_IM_IF):
@@ -2045,7 +2100,7 @@ dispatch:
       while (SCM_NIMP (proc = SCM_CDR (proc)));
       env = EXTEND_ENV (SCM_CAR (x), t.arg1, env);
       x = SCM_CDR (x);
-      goto cdrxnoap;
+      goto nontoplevel_cdrxnoap;
 
 
     case SCM_BIT8(SCM_IM_LETREC):
@@ -2060,7 +2115,7 @@ dispatch:
        }
       while (SCM_NIMP (proc = SCM_CDR (proc)));
       SCM_SETCDR (SCM_CAR (env), t.arg1);
-      goto cdrxnoap;
+      goto nontoplevel_cdrxnoap;
 
 
     case SCM_BIT8(SCM_IM_LETSTAR):
@@ -2069,7 +2124,7 @@ dispatch:
       if (SCM_IMP (proc))
        {
          env = EXTEND_ENV (SCM_EOL, SCM_EOL, env);
-         goto cdrxnoap;
+         goto nontoplevel_cdrxnoap;
        }
       do
        {
@@ -2078,7 +2133,7 @@ dispatch:
          env = EXTEND_ENV (t.arg1, EVALCAR (proc, env), env);
        }
       while (SCM_NIMP (proc = SCM_CDR (proc)));
-      goto cdrxnoap;
+      goto nontoplevel_cdrxnoap;
 
     case SCM_BIT8(SCM_IM_OR):
       x = SCM_CDR (x);
@@ -2175,7 +2230,7 @@ dispatch:
              
              env = EXTEND_ENV (SCM_CAR (SCM_CODE (proc)), argl, SCM_ENV (proc));
              x = SCM_CODE (proc);
-             goto cdrxbegin;
+             goto nontoplevel_cdrxbegin;
            }
          proc = scm_f_apply;
          goto evapply;
@@ -2195,6 +2250,8 @@ dispatch:
          SCM_ASRTGO (SCM_NIMP (proc), badfun);
          PREP_APPLY (proc, scm_cons (t.arg1, SCM_EOL));
          ENTER_APPLY;
+         if (SCM_CLOSUREP(proc) && scm_badformalsp (proc, 1))
+           goto umwrongnumargs;
          goto evap1;
 
        case (SCM_ISYMNUM (SCM_IM_DELAY)):
@@ -2286,7 +2343,7 @@ dispatch:
                                  arg2,
                                  SCM_CMETHOD_ENV (z));
                x = SCM_CMETHOD_CODE (z);
-               goto cdrxbegin;
+               goto nontoplevel_cdrxbegin;
              next_method:
                i = (i + 1) & mask;
              } while (i != end);
@@ -2607,7 +2664,7 @@ evapply:
       case scm_tcs_closures:
        x = SCM_CODE (proc);
        env = EXTEND_ENV (SCM_CAR (x), SCM_EOL, SCM_ENV (proc));
-       goto cdrxbegin;
+       goto nontoplevel_cdrxbegin;
       case scm_tcs_cons_gloc:
        if (SCM_OBJ_CLASS_FLAGS (proc) & SCM_CLASSF_PURE_GENERIC)
          {
@@ -2762,7 +2819,7 @@ evapply:
 #else
          env = EXTEND_ENV (SCM_CAR (x), scm_cons (t.arg1, SCM_EOL), SCM_ENV (proc));
 #endif
-         goto cdrxbegin;
+         goto nontoplevel_cdrxbegin;
        case scm_tcs_cons_gloc:
          if (SCM_OBJ_CLASS_FLAGS (proc) & SCM_CLASSF_PURE_GENERIC)
            {
@@ -2929,7 +2986,7 @@ evapply:
                            scm_cons2 (t.arg1, arg2, SCM_EOL), SCM_ENV (proc));
 #endif
          x = SCM_CODE (proc);
-         goto cdrxbegin;
+         goto nontoplevel_cdrxbegin;
        }
     }
 #ifdef SCM_CAUTIOUS
@@ -3007,7 +3064,7 @@ evapply:
                              debug.info->a.args,
                              SCM_ENV (proc));
        x = SCM_CODE (proc);
-       goto cdrxbegin;
+       goto nontoplevel_cdrxbegin;
 #else /* DEVAL */
       case scm_tc7_subr_3:
        SCM_ASRTGO (SCM_NULLP (SCM_CDR (x)), wrongnumargs);
@@ -3079,7 +3136,7 @@ evapply:
                                         scm_eval_args (x, env, proc)),
                              SCM_ENV (proc));
        x = SCM_CODE (proc);
-       goto cdrxbegin;
+       goto nontoplevel_cdrxbegin;
 #endif /* DEVAL */
       case scm_tcs_cons_gloc:
        if (SCM_OBJ_CLASS_FLAGS (proc) & SCM_CLASSF_PURE_GENERIC)
@@ -3162,8 +3219,14 @@ ret:
    they're referring to, send me a patch to this comment.  */
 
 SCM_DEFINE (scm_nconc2last, "apply:nconc2last", 1, 0, 0, 
-           (SCM lst),
-           "")
+           (SCM lst),
+           "Given a list (@var{arg1} @dots{} @var{args}), this function\n"
+           "conses the @var{arg1} @dots{} arguments onto the front of\n"
+           "@var{args}, and returns the resulting list. Note that\n"
+           "@var{args} is a list; thus, the argument to this function is\n"
+           "a list whose last element is a list.\n"
+           "Note: Rather than do new consing, @code{apply:nconc2last}\n"
+           "destroys its argument, so use with care.")
 #define FUNC_NAME s_scm_nconc2last
 {
   SCM *lloc;
@@ -3301,6 +3364,7 @@ tail:
       SCM_ASRTGO (SCM_UNBNDP (arg1), wrongnumargs);
       RETURN (SCM_SUBRF (proc) ())
     case scm_tc7_subr_1:
+      SCM_ASRTGO (!SCM_UNBNDP (arg1), wrongnumargs);
     case scm_tc7_subr_1o:
       SCM_ASRTGO (SCM_NULLP (args), wrongnumargs);
       RETURN (SCM_SUBRF (proc) (arg1))
@@ -3337,6 +3401,10 @@ tail:
        RETURN (arg1)
       }
     case scm_tc7_subr_3:
+      SCM_ASRTGO (SCM_NNULLP (args)
+                 && SCM_NNULLP (SCM_CDR (args))
+                 && SCM_NULLP (SCM_CDDR (args)),
+                 wrongnumargs);
       RETURN (SCM_SUBRF (proc) (arg1, SCM_CAR (args), SCM_CAR (SCM_CDR (args))))
     case scm_tc7_lsubr:
 #ifdef DEVAL
@@ -3408,6 +3476,8 @@ tail:
                  proc = scm_m_expand_body (proc, args);
                  goto again;
                }
+             else
+               SCM_EVALIM2 (SCM_CAR (proc));
            }
          else
            SCM_CEVAL (SCM_CAR (proc), args);
@@ -3754,7 +3824,7 @@ SCM_DEFINE (scm_copy_tree, "copy-tree", 1, 0, 0,
   if (SCM_VECTORP (obj))
     {
       scm_sizet i = SCM_VECTOR_LENGTH (obj);
-      ans = scm_make_vector (SCM_MAKINUM (i), SCM_UNSPECIFIED);
+      ans = scm_c_make_vector (i, SCM_UNSPECIFIED);
       while (i--)
        SCM_VELTS (ans)[i] = scm_copy_tree (SCM_VELTS (obj)[i]);
       return ans;
@@ -3776,8 +3846,47 @@ SCM_DEFINE (scm_copy_tree, "copy-tree", 1, 0, 0,
 #undef FUNC_NAME
 
 
+/* We have three levels of EVAL here:
+
+   - scm_i_eval (exp, env)
+
+     evaluates EXP in environment ENV.  ENV is a lexical environment
+     structure as used by the actual tree code evaluator.  When ENV is
+     a top-level environment, then changes to the current module are
+     tracked by modifying ENV so that it continues to be in sync with
+     the current module.
+
+   - scm_primitive_eval (exp)
+
+     evaluates EXP in the top-level environment as determined by the
+     current module.  This is done by constructing a suitable
+     environment and calling scm_i_eval.  Thus, changes to the
+     top-level module are tracked normally.
+
+   - scm_eval (exp, mod)
+
+     evaluates EXP while MOD is the current module.  Thius is done by
+     setting the current module to MOD, invoking scm_primitive_eval on
+     EXP, and then restoring the current module to the value it had
+     previously.  That is, while EXP is evaluated, changes to the
+     current module are tracked, but these changes do not persist when
+     scm_eval returns.
+
+  For each level of evals, there are two variants, distinguished by a
+  _x suffix: the ordinary variant does not modify EXP while the _x
+  variant can destructively modify EXP into something completely
+  unintelligible.  A Scheme data structure passed as EXP to one of the
+  _x variants should not ever be used again for anything.  So when in
+  doubt, use the ordinary variant.
+
+*/
+
 SCM scm_system_transformer;
 
+/* XXX - scm_i_eval is meant to be useable for evaluation in
+   non-toplevel environments, for example when used by the debugger.
+   Can the system transform deal with this? */
+
 SCM 
 scm_i_eval_x (SCM exp, SCM env)
 {
@@ -3793,41 +3902,41 @@ scm_i_eval (SCM exp, SCM env)
   SCM transformer = scm_fluid_ref (SCM_CDR (scm_system_transformer));
   if (SCM_NIMP (transformer))
     exp = scm_apply (transformer, exp, scm_listofnull);
-  return SCM_XEVAL (scm_copy_tree (exp), env);
+  exp = scm_copy_tree (exp);
+  return SCM_XEVAL (exp, env);
 }
 
 SCM
-scm_eval_x (SCM exp, SCM module)
+scm_primitive_eval_x (SCM exp)
 {
-  return scm_i_eval_x (exp,
-                      scm_top_level_env (SCM_MODULE_EVAL_CLOSURE (module)));
+  SCM env = scm_top_level_env (scm_current_module_lookup_closure ());
+  return scm_i_eval_x (exp, env);
 }
 
+SCM_DEFINE (scm_primitive_eval, "primitive-eval", 1, 0, 0,
+           (SCM exp),
+           "Evaluate @var{exp} in the top-level environment specified by\n"
+           "the current module.")
+#define FUNC_NAME s_scm_primitive_eval
+{
+  SCM env = scm_top_level_env (scm_current_module_lookup_closure ());
+  return scm_i_eval (exp, env);
+}
+#undef FUNC_NAME
+
 /* Eval does not take the second arg optionally.  This is intentional
  * in order to be R5RS compatible, and to prepare for the new module
  * system, where we would like to make the choice of evaluation
- * environment explicit.
- */
+ * environment explicit.  */
 
 static void
 change_environment (void *data)
 {
   SCM pair = SCM_PACK (data);
   SCM new_module = SCM_CAR (pair);
-  SCM old_module = scm_selected_module ();
+  SCM old_module = scm_current_module ();
   SCM_SETCDR (pair, old_module);
-  scm_select_module (new_module);
-}
-
-
-static SCM
-inner_eval (void *data)
-{
-  SCM pair = SCM_PACK (data);
-  SCM exp = SCM_CAR (pair);
-  SCM env = SCM_CDR (pair);
-  SCM result = scm_i_eval (exp, env);
-  return result;
+  scm_set_current_module (new_module);
 }
 
 
@@ -3836,36 +3945,57 @@ restore_environment (void *data)
 {
   SCM pair = SCM_PACK (data);
   SCM old_module = SCM_CDR (pair);
-  SCM new_module = scm_selected_module ();
+  SCM new_module = scm_current_module ();
   SCM_SETCAR (pair, new_module);
-  scm_select_module (old_module);
+  scm_set_current_module (old_module);
 }
 
+static SCM
+inner_eval_x (void *data)
+{
+  return scm_primitive_eval_x (SCM_PACK(data));
+}
 
-SCM_DEFINE (scm_eval, "eval", 2, 0, 0, 
-           (SCM exp, SCM environment),
-           "Evaluate @var{exp}, a list representing a Scheme expression, in the\n"
-           "environment given by @var{environment specifier}.")
-#define FUNC_NAME s_scm_eval
+SCM
+scm_eval_x (SCM exp, SCM module)
+#define FUNC_NAME "eval!"
 {
-  SCM copied_exp;
-  SCM env_closure;
+  SCM_VALIDATE_MODULE (2, module);
 
-  SCM_VALIDATE_MODULE (2, environment);
+  return scm_internal_dynamic_wind 
+    (change_environment, inner_eval_x, restore_environment,
+     (void *) SCM_UNPACK (exp),
+     (void *) SCM_UNPACK (scm_cons (module, SCM_BOOL_F)));
+}
+#undef FUNC_NAME
 
-  copied_exp = scm_copy_tree (exp);
-  env_closure = scm_top_level_env (SCM_MODULE_EVAL_CLOSURE (environment));
+static SCM
+inner_eval (void *data)
+{
+  return scm_primitive_eval (SCM_PACK(data));
+}
+
+SCM_DEFINE (scm_eval, "eval", 2, 0, 0, 
+           (SCM exp, SCM module),
+           "Evaluate @var{exp}, a list representing a Scheme expression,\n"
+            "in the top-level environment specified by @var{module}.\n"
+            "While @var{exp} is evaluated (using @var{primitive-eval}),\n"
+            "@var{module} is made the current module.  The current module\n"
+            "is reset to its previous value when @var{eval} returns.")
+#define FUNC_NAME s_scm_eval
+{
+  SCM_VALIDATE_MODULE (2, module);
 
   return scm_internal_dynamic_wind 
     (change_environment, inner_eval, restore_environment,
-     (void *) SCM_UNPACK (scm_cons (copied_exp, env_closure)),
-     (void *) SCM_UNPACK (scm_cons (environment, SCM_BOOL_F)));
+     (void *) SCM_UNPACK (exp),
+     (void *) SCM_UNPACK (scm_cons (module, SCM_BOOL_F)));
 }
 #undef FUNC_NAME
 
 #if (SCM_DEBUG_DEPRECATED == 0)
 
-/* Use scm_selected_module () or scm_interaction_environment ()
+/* Use scm_current_module () or scm_interaction_environment ()
  * instead.  The former is the module selected during loading of code.
  * The latter is the module in which the user of this thread currently
  * types expressions.
@@ -3876,7 +4006,8 @@ SCM scm_top_level_lookup_closure_var;
 /* Avoid using this functionality altogether (except for implementing
  * libguile, where you can use scm_i_eval or scm_i_eval_x).
  *
- * Applications should use either C level scm_eval_x or Scheme scm_eval.  */
+ * Applications should use either C level scm_eval_x or Scheme
+ * scm_eval; or scm_primitive_eval_x or scm_primitive_eval.  */
 
 SCM 
 scm_eval_3 (SCM obj, int copyp, SCM env)
@@ -3889,9 +4020,11 @@ scm_eval_3 (SCM obj, int copyp, SCM env)
 
 SCM_DEFINE (scm_eval2, "eval2", 2, 0, 0,
            (SCM obj, SCM env_thunk),
-           "Evaluate @var{exp}, a Scheme expression, in the environment designated\n"
-           "by @var{lookup}, a symbol-lookup function.  @code{(eval exp)} is\n"
-           "equivalent to @code{(eval2 exp *top-level-lookup-closure*)}.")
+           "Evaluate @var{exp}, a Scheme expression, in the environment\n"
+           "designated by @var{lookup}, a symbol-lookup function."
+           "Do not use this version of eval, it does not play well\n"
+           "with the module system.  Use @code{eval} or\n"
+           "@code{primitive-eval} instead.")
 #define FUNC_NAME s_scm_eval2
 {
   return scm_i_eval (obj, scm_top_level_env (env_thunk));