summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2018-11-13 20:45:10 -0800
committerKaz Kylheku <kaz@kylheku.com>2018-11-13 20:45:10 -0800
commit8e2aef8f2b466b37753ba0acd4bd668ff54b3669 (patch)
tree54380c2dc9a0565f9fdc25349a68552094d1e785
parent156004248adbb220505567309dfb1fc22f7ab5e4 (diff)
downloadtxr-8e2aef8f2b466b37753ba0acd4bd668ff54b3669.tar.gz
txr-8e2aef8f2b466b37753ba0acd4bd668ff54b3669.tar.bz2
txr-8e2aef8f2b466b37753ba0acd4bd668ff54b3669.zip
copy-fun: duplicate a function, with own environment.
* eval.c (deep_copy_env): New function. (eval_init): Register copy-fun intrinsic. * eval.h (deep_copy_env): Declared. * lib.c (copy_fun): New function. * lib.h (copy_fun): Declared. * vm.c (vm_copy_closure): New function. * vm.h (vm_copy_closure): Declared. * txr.1: Documented copy-fun.
-rw-r--r--eval.c17
-rw-r--r--eval.h1
-rw-r--r--lib.c15
-rw-r--r--lib.h1
-rw-r--r--txr.136
-rw-r--r--vm.c26
-rw-r--r--vm.h1
7 files changed, 97 insertions, 0 deletions
diff --git a/eval.c b/eval.c
index 32e26b53..6053c853 100644
--- a/eval.c
+++ b/eval.c
@@ -138,6 +138,22 @@ val copy_env(val oenv)
}
}
+val deep_copy_env(val oenv)
+{
+ type_check(lit("deep-copy-env"), oenv, ENV);
+
+ {
+ val nenv = make_obj();
+ nenv->e.type = ENV;
+ nenv->e.vbindings = copy_alist(oenv->e.vbindings);
+ nenv->e.fbindings = copy_alist(oenv->e.fbindings);
+
+ nenv->e.up_env = if2(oenv->e.up_env != nil,
+ deep_copy_env(oenv->e.up_env));
+ return nenv;
+ }
+}
+
/*
* Wrapper for performance reasons: don't make make_env
* process default arguments.
@@ -6745,6 +6761,7 @@ void eval_init(void)
reg_fun(intern(lit("special-operator-p"), user_package), func_n1(special_operator_p));
reg_fun(intern(lit("special-var-p"), user_package), func_n1(special_var_p));
reg_fun(sys_mark_special_s, func_n1(mark_special));
+ reg_fun(intern(lit("copy-fun"), user_package), func_n1(copy_fun));
reg_fun(intern(lit("func-get-form"), user_package), func_n1(func_get_form));
reg_fun(intern(lit("func-get-name"), user_package), func_n2o(func_get_name, 1));
reg_fun(intern(lit("func-get-env"), user_package), func_n1(func_get_env));
diff --git a/eval.h b/eval.h
index 6a69714d..d85d9fb6 100644
--- a/eval.h
+++ b/eval.h
@@ -44,6 +44,7 @@ val set_last_form_evaled(val form);
void error_trace(val exsym, val exvals, val out_stream, val prefix);
val make_env(val fbindings, val vbindings, val up_env);
val copy_env(val oenv);
+val deep_copy_env(val oenv);
val env_fbind(val env, val sym, val fun);
val env_vbind(val env, val sym, val obj);
val lookup_var(val env, val sym);
diff --git a/lib.c b/lib.c
index 33eddfe9..3d17a562 100644
--- a/lib.c
+++ b/lib.c
@@ -6129,6 +6129,21 @@ val func_vm(val closure, val desc, int fixparam, int reqargs, int variadic)
return obj;
}
+val copy_fun(val ofun)
+{
+ val self = lit("copy-fun");
+ type_check(self, ofun, FUN);
+ {
+ val nfun = make_obj();
+ nfun->f = ofun->f;
+
+ if (nfun->f.env)
+ nfun->f.env = if3(nfun->f.functype == FVM,
+ vm_copy_closure, deep_copy_env)(nfun->f.env);
+ return nfun;
+ }
+}
+
val func_get_form(val fun)
{
val self = lit("func-get-form");
diff --git a/lib.h b/lib.h
index 1d6488d3..c6973252 100644
--- a/lib.h
+++ b/lib.h
@@ -904,6 +904,7 @@ val func_n2ov(val (*fun)(val, val, varg), int reqargs);
val func_n3ov(val (*fun)(val, val, val, varg), int reqargs);
val func_interp(val env, val form);
val func_vm(val closure, val desc, int fixparam, int reqargs, int variadic);
+val copy_fun(val ofun);
val func_get_form(val fun);
val func_get_env(val fun);
val func_set_env(val fun, val env);
diff --git a/txr.1 b/txr.1
index 22a1913b..1aac81f8 100644
--- a/txr.1
+++ b/txr.1
@@ -14207,6 +14207,42 @@ argument is not given any special syntactic treatment at all)
while the Lisp-2 foundation provides a traditional Lisp environment with its
"natural hygiene".
+.coNP Function @ copy-fun
+.synb
+.mets (copy-fun << function )
+.syne
+.desc
+The
+.code copy-fun
+function produces and returns a duplicate of
+.metn function ,
+which must be a function.
+
+A duplicate of a function is a distinct function object not
+.code eq
+to the original function, yet which accepts the same arguments
+and behaves exactly the same way as the original.
+
+If a function contains no captured environment, then a copy made of that
+function by
+.code copy-fun
+is indistinguishable from the original function in every regard,
+except for being a distinct object that compares unequal to the original
+under the
+.code eq
+function.
+
+If a function contains a captured environment, then a copy of that function
+made by
+.code copy-fun
+has its own copy of that environment. If the copied function changes the
+values of captured lexical variables, the original function is not affected by
+these changes and
+.IR "vice versa" .
+
+The entire lexical environment is copied; the copy and original function do not
+share any portion of the environment at any level of nesting.
+
.SS* Sequencing, Selection and Iteration
.coNP Operators @ progn and @ prog1
.synb
diff --git a/vm.c b/vm.c
index c5a0ed17..9322cba8 100644
--- a/vm.c
+++ b/vm.c
@@ -292,6 +292,32 @@ static val vm_make_closure(struct vm *vm, int frsz)
return closure;
}
+val vm_copy_closure(val oclosure)
+{
+ struct vm_closure *ovc = coerce(struct vm_closure *, oclosure->co.handle);
+ const size_t hdr_sz = offsetof (struct vm_closure, dspl);
+ size_t dspl_sz = ovc->nlvl * sizeof (struct vm_env);
+ struct vm_closure *nvc = coerce(struct vm_closure *,
+ chk_malloc(hdr_sz + dspl_sz));
+ val nclosure;
+ int i;
+
+ memcpy(nvc, ovc, hdr_sz + dspl_sz);
+
+ nclosure = cobj(coerce(mem_t *, nvc), vm_closure_s, &vm_closure_ops);
+
+ for (i = 2; i < nvc->nlvl; i++) {
+ struct vm_env *ndi = &nvc->dspl[i];
+
+ if (ndi->vec != nil) {
+ ndi->vec = copy_vec(ndi->vec);
+ ndi->mem = ndi->vec->v.vec;
+ }
+ }
+
+ return nclosure;
+}
+
static void vm_closure_mark(val obj)
{
struct vm_closure *vc = coerce(struct vm_closure *, obj->co.handle);
diff --git a/vm.h b/vm.h
index 30720d0d..e39b115f 100644
--- a/vm.h
+++ b/vm.h
@@ -30,6 +30,7 @@ extern val vm_desc_s, vm_closure_s;
val vm_make_desc(val nlevels, val nregs, val bytecode,
val datavec, val funvec);
val vm_execute_toplevel(val desc);
+val vm_copy_closure(val closure);
val vm_execute_closure(val fun, struct args *);
void vm_invalidate_binding(val sym);
void vm_init(void);