summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2014-01-29 06:58:15 -0800
committerKaz Kylheku <kaz@kylheku.com>2014-01-29 06:58:15 -0800
commitff17df5e47559aa87ffd828c6126a63165805ad2 (patch)
tree3a0aa3cf65ed8ff39356fd1a8a5eaf78d7dbfefb
parentb8966f0bf73f01e1e380a08e6949ccc9ddd45637 (diff)
downloadtxr-ff17df5e47559aa87ffd828c6126a63165805ad2.tar.gz
txr-ff17df5e47559aa87ffd828c6126a63165805ad2.tar.bz2
txr-ff17df5e47559aa87ffd828c6126a63165805ad2.zip
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.
-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: