summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2021-04-28 20:41:22 -0700
committerKaz Kylheku <kaz@kylheku.com>2021-04-28 20:41:22 -0700
commit48ffaab4f43b4f6a2d550f08722dd9558adbf5e1 (patch)
tree4197dcbfea55b397d72fb70669c0c0c17fea5d4c
parent44aa252186ff37ab871c5b66e62ad26abeb5b385 (diff)
downloadtxr-48ffaab4f43b4f6a2d550f08722dd9558adbf5e1.tar.gz
txr-48ffaab4f43b4f6a2d550f08722dd9558adbf5e1.tar.bz2
txr-48ffaab4f43b4f6a2d550f08722dd9558adbf5e1.zip
macros: lexical-var-p: don't report t for specials.
The lexical-var-p function wrongly reports true for locally rebound special variables, which are not lexical. * eval.c (special_var_p): Static function moved to avoid forward declaration. (lexical_var_p): Bail if sym satisfies special_var_p. (old_lexical_var_p): New function, copy of old lexical_var_p before this bugfix. (eval_init): Conditionally register lexical-var-p as either lexical_var_p or old_lexical_var_p depending on the compat value. * txr.1: Update documentation for lexical-var-p to clarify that it doesn't report true for specials. Also make note that it doesn't report true for global lexicals, and likewise that lexical-fun-p doesn't report global functions. Added compat note.
-rw-r--r--eval.c38
-rw-r--r--txr.156
2 files changed, 82 insertions, 12 deletions
diff --git a/eval.c b/eval.c
index 5983f2cd..b2e0605a 100644
--- a/eval.c
+++ b/eval.c
@@ -715,10 +715,19 @@ static val reparent_env(val child, val parent)
return child;
}
+static val special_var_p(val sym)
+{
+ uses_or2;
+ return or2(gethash(special, sym),
+ if2(lisplib_try_load(sym), gethash(special, sym)));
+}
+
static val lexical_var_p(val menv, val sym)
{
if (nilp(menv)) {
return nil;
+ } else if (special_var_p(sym)) {
+ return nil;
} else {
type_check(lit("lexical-var-p"), menv, ENV);
@@ -732,6 +741,23 @@ static val lexical_var_p(val menv, val sym)
}
}
+static val old_lexical_var_p(val menv, val sym)
+{
+ if (nilp(menv)) {
+ return nil;
+ } else {
+ type_check(lit("lexical-var-p"), menv, ENV);
+
+ {
+ val binding = assoc(sym, menv->e.vbindings);
+
+ if (binding)
+ return tnil(cdr(binding) == special_s);
+ return lexical_var_p(menv->e.up_env, sym);
+ }
+ }
+}
+
static val lexical_fun_p(val menv, val sym)
{
if (nilp(menv)) {
@@ -780,13 +806,6 @@ static val mark_special(val sym)
return sethash(special, sym, t);
}
-static val special_var_p(val sym)
-{
- uses_or2;
- return or2(gethash(special, sym),
- if2(lisplib_try_load(sym), gethash(special, sym)));
-}
-
static void copy_env_handler(mem_t *ptr)
{
val *penv = coerce(val *, ptr);
@@ -6793,7 +6812,10 @@ void eval_init(void)
reg_fun(intern(lit("env-vbindings"), user_package), func_n1(env_vbindings));
reg_fun(intern(lit("env-fbindings"), user_package), func_n1(env_fbindings));
reg_fun(intern(lit("env-next"), user_package), func_n1(env_next));
- reg_fun(intern(lit("lexical-var-p"), user_package), func_n2(lexical_var_p));
+ reg_fun(intern(lit("lexical-var-p"), user_package),
+ func_n2(if3(opt_compat && opt_compat <= 257,
+ old_lexical_var_p,
+ lexical_var_p)));
reg_fun(intern(lit("lexical-fun-p"), user_package), func_n2(lexical_fun_p));
reg_fun(intern(lit("lexical-lisp1-binding"), user_package),
func_n2(lexical_lisp1_binding));
diff --git a/txr.1 b/txr.1
index c3524bfa..ddf1f105 100644
--- a/txr.1
+++ b/txr.1
@@ -36785,11 +36785,45 @@ parameter. Using these functions, a macro can enquire whether
a given
.meta form
is a symbol which has a variable binding or a function binding
-in the lexical environment.
+in the local lexical environment.
This information is known during macro expansion. The macro expander
recognizes lexical function and variable bindings, because these
bindings can shadow macros.
+Special variables are not lexical. The function
+.code lexical-var-p
+returns
+.code nil
+if
+.meta form
+satisfies
+.code special-var-p
+function, indicating that it is the name of a special variable.
+
+The
+.code lexical-var-p
+function also returns
+.code nil
+for global lexical variables. If
+.meta form
+is a symbol for which only a global lexical variable binding is apparent,
+.code lexical-var-p
+returns
+.codn nil .
+Testing for the existence for a global variable can be done using
+.codn boundp ;
+if a symbol is
+.code boundp
+but not
+.codn special-var-p ,
+then it is a global lexical variable.
+
+Similarly,
+.code lexical-fun-p
+returns
+.code nil
+for global functions.
+
.TP* Example:
.verb
@@ -36806,14 +36840,20 @@ bindings can shadow macros.
(t :not-lex-fun-var)))
;;
- ;; This returns:
- ;;
- ;; (:lexical-var :not-lex-fun-var :lexical-fun)
+ ;; Use classify macro above to report classification
+ ;; of the x, y and f symbols in the given scope
;;
(let ((x 1) (y 2))
(symacrolet ((y x))
(flet ((f () (+ 2 2)))
(list (classify x) (classify y) (classify f)))))
+ --> (:lexical-var :not-lex-fun-var :lexical-fun)
+
+ ;; Locally bound specials are not lexical
+
+ (let ((*stdout* *stdnull*))
+ (classify *stdout*))
+ --> :not-lex-fun-var
.brev
.TP* Note:
@@ -79273,6 +79313,14 @@ A compatibility value of 248 or lower restores the above old behaviors of
.code @
and
.codn hash-revget .
+.IP 257
+Until \*(TX 257, the function
+.code lexical-var-p
+returned
+.code t
+for not only lexical variables, but also for locally bound special variables,
+which are not lexical. The behavior is restored if 257 or older compatibility
+is selected.
.IP 244
Until \*(TX 244, the
.code env-hash