summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--eval.c25
-rw-r--r--txr.124
3 files changed, 50 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index f1435385..903c1045 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2014-01-29 Kaz Kylheku <kaz@kylheku.com>
+
+ Default argument initializer scoping rule change, allowing
+ things like (defun foo (s : (l (length s))) ...).
+ Default arguments can be initialized by expressions that
+ refer to the arguments.
+
+ * eval.c (bind_args): By means of a local array, defer the evaluation
+ of optional argument init forms until the lexical environment,
+ including all the parameters, is captured. Then valuates the forms
+ in the array, and set the variable values.
+
+ * txr.1: Clarify the new scoping rules.
+
2014-01-28 Kaz Kylheku <kaz@kylheku.com>
* eval.c (eval_init): Registered existing make_catenated_stream as
diff --git a/eval.c b/eval.c
index 1cfd76f6..142776fa 100644
--- a/eval.c
+++ b/eval.c
@@ -220,8 +220,11 @@ static val lookup_sym_lisp1(val env, val sym)
static val bind_args(val env, val params, val args, val ctx_form)
{
+ val opt_init_parm[32];
+ val *oi_parm = &opt_init_parm[0], *oi_end = &opt_init_parm[32], *oi_iter;
val new_bindings = nil;
val optargs = nil;
+ val new_env;
for (; args && consp(params); args = cdr(args), params = cdr(params)) {
val param = car(params);
@@ -259,15 +262,18 @@ static val bind_args(val env, val params, val args, val ctx_form)
eval_error(ctx_form, lit("~s: too few arguments"), car(ctx_form), nao);
while (consp(params)) {
val param = car(params);
- val val = nil;
if (param == colon_k)
goto twocol;
if (consp(param)) {
- val = car(cdr(param));
- param = car(param);
+ if (oi_parm == oi_end)
+ eval_error(ctx_form,
+ lit("~s: too many optional args with initializers"),
+ car(ctx_form), nao);
+ new_bindings = acons(car(param), car(cdr(param)), new_bindings);
+ *oi_parm++ = car(new_bindings);
+ } else {
+ new_bindings = acons(param, nil, new_bindings);
}
- new_bindings = acons(param, eval(val, env, ctx_form),
- new_bindings);
params = cdr(params);
}
if (bindable(params))
@@ -279,7 +285,14 @@ static val bind_args(val env, val params, val args, val ctx_form)
eval_error(ctx_form, lit("~s: too many arguments"), car(ctx_form), nao);
}
- return make_env(new_bindings, nil, env);
+ new_env = make_env(new_bindings, nil, env);
+
+ for (oi_iter = &opt_init_parm[0]; oi_iter < oi_parm; oi_iter++) {
+ val initval = eval(cdr(*oi_iter), new_env, ctx_form);
+ rplacd(*oi_iter, initval);
+ }
+
+ return new_env;
twocol:
eval_error(ctx_form, lit("~a: multiple colons in parameter list"),
car(ctx_form), nao);
diff --git a/txr.1 b/txr.1
index 89148672..f92bedba 100644
--- a/txr.1
+++ b/txr.1
@@ -5912,19 +5912,29 @@ The dotted notation can be used to write a function that accepts
a variable number of arguments. To write a function that accepts
variable arguments only, with no required arguments, use a single symbol.
-The keyword symbol : can appear in the parameter list. It is not an argument,
-but a separator between required parameters and optional parameters.
-When the function is called, optional parameter for which arguments
-are not supplied take on the value nil.
+The keyword symbol : (colon) can appear in the parameter list. It is not an
+argument, but a separator between required parameters and optional parameters.
+When the function is called, optional parameter for which arguments are not
+supplied take on the value nil.
An optional parameter can also be written in the form (<name> <expr>).
In this situation, if the call does not specify a value for the parameter,
then the parameter takes on the value of the expression <expr>.
-The expression is evaluated in the environment in which the lambda
-was constructed.
-Functions created by lambda capture the surrounding variable bindings.
+The initializer expressions are evaluated an environment in which
+all of the parameters are visible, which extends the environment in
+which the lambda was constructed. For instance:
+ (let ((default 0))
+ (lambda (str : (end (length str)) (counter default))
+ (list str end counter)))
+
+In this lambda, the initializing expression for the optional parameter
+end is (length str), and the str variable it refers to is the previous
+argument. The initializer for the optional variable counter is
+the expression default, and it refers to the binding established
+by the surrounding let. This reference is captured as part of the
+lambda's lexical closure.
.TP
Examples: