guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Implement local-eval, local-compile, and the-environment


From: Mark H Weaver
Subject: [PATCH] Implement local-eval, local-compile, and the-environment
Date: Tue, 03 Jan 2012 05:52:15 -0500

Hello all,

I have now produced two very different implementations of
`the-environment' and `local-eval' that support compilation.

Included below is a simple patch that I believe is ready for commit to
the stable-2.0 branch, and I very much hope it can be included in 2.0.4.

However, before I tell you about this nice simple patch, I'll briefly
mention the other more complex (and more efficient) compiler
implementation.

* * * * *

A few days ago I finished a working implementation that is fully
integrated into all passes of the compiler, and with a completely
separate evaluator implementation (which I've already posted).  I wrote
it in such a way that other languages could easily implement similar
functionality, by separating the lexical environment object into two
layers: one layer for tree-il that would be appropriate for any language
that compiles to tree-il, and one layer for scheme (macro expansion).

It was also implemented with efficiency in mind: `local-eval', when
applied to a lexical environment produced by compiled code, produces a
closure with free variables attached, so the only performance loss is
due to the fact that all reachable lexicals must be boxed.

Also, it was implemented such that `local-eval' for compiled lexical
environments simply does this: (compile x #:env e).  This involved
changing the representation of compile environments to support
structures other than simple modules, which is arguably a good idea
independently of the needs of `local-eval', before the assumption that
(compiler environments == modules) becomes too deeply entrenched.

However, in the end, I felt that this implementation was more complex
than I'd prefer, and too spread out.  Future work on the compiler would
always need to keep `the-environment' in mind, and it could easily
become broken in subtle ways if future implementers weren't careful.
I may work on it again in the future, but for now I've decided to use a
different approach.

* * * * *

The patch attached below implements `the-environment' and `local-eval'
in a much simpler way, though not quite as efficiently.  It is
implemented entirely in psyntax and in a new module (ice-9 local-eval).
No changes were needed to the compiler, and no new tree-il forms are
needed.

The idea is similar to the general dispatcher that I proposed earlier,
except that here the dispatcher is not a mere trick to fool the
compiler; it really is the mechanism used by local-eval to access the
captured variables.  Also, instead of making a single dispatcher for all
variables, I give each variable its own dispatcher, which I call a `box'
(though that name is not present in any public interfaces or
documentation).

  (define-syntax-rule (box v)
    (case-lambda
     (() v)
     ((x) (set! v x))))

Then I have something called `box-lambda' that is like `lambda' except
that the resulting procedure accepts these boxes as arguments, and uses
variable transformers to simulate normal variable bindings within:

  (define-syntax-rule (box-lambda (v ...) e)
    (lambda (v ...)
      (let-syntax
          ((v (identifier-syntax-from-box v))
           ...)
        (if #t e))))

  (define-syntax-rule (identifier-syntax-from-box b)
    (let ((trans (identifier-syntax
                  (id          (b))
                  ((set! id x) (b x)))))
      (set-procedure-property! trans
                               'identifier-syntax-box
                               (syntax-object-of b))
      trans))

The captured lexical environment includes a "wrapper" procedure that
takes an expression (or syntax-object) and wraps it within box-lambda*
(a slightly more complex variant of box-lambda).  `local-eval' simply
calls the wrapper, evaluates or compiles the wrapped expression, and
then applies the resulting procedure to the boxes.

This of course means that these variable references are transformed into
procedure calls, so there's an added cost.  A naive implementation would
cost O(n) where N is the box-lambda* nesting depth, but I added an
optimization so that we never nest boxes.  The first box is always
reused, so the cost is O(1).

Note that this approach means that we no longer need to capture or
serialize the internal psyntax data structures.  Unfortunately, in the
current patch, local syntax transformers are not supported, but I have a
plan for how to do that, at least for `syntax-rules' macros, which are
specially marked using procedure meta-data.

The idea is to attach the source code for `syntax-rules' macros to the
resulting transformer procedure using procedure properties.  Then
`the-environment' can create a more complex wrapping procedure that
creates a set of nested `let-syntax' and `letrec-syntax' forms,
replacing the single `let-syntax' created by `box-lambda*'.

In practice, the vast majority of procedural macros could be handled
this way as well, though unfortunately the emulation would not be
perfect in all cases, e.g. if the transformer includes local state.

In the current patch, only lexical _variables_ are captured, though the
list of unsupported lexical bindings is saved, and `box-lambda*'
arranges to throw an error at compile time if they are referenced.

I also added a new interface called `local-compile' that compiles the
local expression instead of using `eval'.

Anyway, here's the patch.  As before, I cut out the psyntax-pp.scm
patch, since it's so huge.  Regenerate it with:

  make -C module ice-9/psyntax-pp.scm.gen

Comments and suggestions solicited.

    Best,
     Mark

>From 8017c7b688ce65c12e125ead6be271b6e7d3c88e Mon Sep 17 00:00:00 2001
From: Mark H Weaver <address@hidden>
Date: Tue, 3 Jan 2012 04:02:08 -0500
Subject: [PATCH] Implement `local-eval', `local-compile', and
 `the-environment'

* module/ice-9/local-eval.scm: New module (ice-9 local-eval) which
  exports `local-eval' and `local-compile'.  This module also contains
  (non-exported) syntax transformers used internally by psyntax to
  implement `the-environment'.

* module/ice-9/psyntax.scm: New core syntax form `the-environment'.
  New internal procedure `reachable-sym+labels' generates the list
  of lexical bindings reachable using normal symbols (as opposed to
  syntax objects which could reach a larger set of bindings).

* libguile/debug.c (scm_local_eval): New C function that calls the
  Scheme implementation of `local-eval' in (ice-9 local-eval).

* libguile/debug.h (scm_local_eval): Add prototype.

* doc/ref/api-evaluation.texi (Local Evaluation): Add documentation.

* test-suite/tests/eval.test (local evaluation): Add tests.

* test-suite/standalone/test-loose-ends.c (test_scm_local_eval):
  Add test.

* module/Makefile.am: Add ice-9/local-eval.scm.

* module/ice-9/psyntax-pp.scm: Regenerate from psyntax.scm.
---
 doc/ref/api-evaluation.texi             |   41 +
 libguile/debug.c                        |   11 +
 libguile/debug.h                        |    2 +
 module/Makefile.am                      |    3 +-
 module/ice-9/local-eval.scm             |  110 +
 module/ice-9/psyntax-pp.scm             |13902 ++++++++++++++++---------------
 module/ice-9/psyntax.scm                |  114 +
 test-suite/standalone/test-loose-ends.c |   14 +
 test-suite/tests/eval.test              |   66 +-
 9 files changed, 7487 insertions(+), 6776 deletions(-)
 create mode 100644 module/ice-9/local-eval.scm

diff --git a/doc/ref/api-evaluation.texi b/doc/ref/api-evaluation.texi
index 6a09bef..5eb6669 100644
--- a/doc/ref/api-evaluation.texi
+++ b/doc/ref/api-evaluation.texi
@@ -19,6 +19,7 @@ loading, evaluating, and compiling Scheme code at run time.
 * Loading::                     Loading Scheme code from file.
 * Character Encoding of Source Files:: Loading non-ASCII Scheme code from file.
 * Delayed Evaluation::          Postponing evaluation until it is needed.
+* Local Evaluation::            Evaluation in a local lexical environment.
 @end menu
 
 
@@ -952,6 +953,46 @@ value.
 @end deffn
 
 
address@hidden Local Evaluation
address@hidden Local Evaluation
+
address@hidden syntax the-environment
+Captures and returns a lexical environment object for use with
address@hidden or @code{local-compile}.
address@hidden deffn
+
address@hidden {Scheme Procedure} local-eval exp env
address@hidden {C Function} scm_local_eval (exp, env)
+Evaluate the expression @var{exp} in the lexical environment @var{env}.
+This mostly behaves as if @var{exp} had been wrapped in a lambda
+expression @code{(lambda () @var{exp})} and put in place of
address@hidden(the-environment)}, with the resulting procedure called by
address@hidden  In other words, @var{exp} is evaluated within the
+lexical environment of @code{(the-environment)}, but within the dynamic
+environment of @code{local-eval}.
address@hidden deffn
+
address@hidden {Scheme Procedure} local-compile exp env [to=value] [opts=()]
+Compile the expression @var{exp} in the lexical environment @var{env}.
+If @var{exp} is a procedure, the result will be a compiled procedure;
+otherwise @code{local-compile} is mostly equivalent to
address@hidden  @var{to} and @var{opts} specify the target language
+and compilation options.  @xref{Compiling to the Virtual Machine}, for
+more information.
address@hidden deffn
+
+Note that the current implementation of @code{(the-environment)} has
+some limitations.  It does not capture local syntax transformers bound
+by @code{let-syntax}, @code{letrec-syntax} or non-top-level
address@hidden forms.  It also does not capture pattern variables
+bound by @code{syntax-case}.  Any attempt to reference such captured
+bindings via @code{local-eval} or @code{local-compile} produces an
+error.  Finally, @code{(the-environment)} does not capture lexical
+variables that are shadowed by inner bindings with the same name, nor
+hidden lexical bindings produced by macro expansion, even though such
+variables might be accessible using syntax objects.
+
+
 @c Local Variables:
 @c TeX-master: "guile.texi"
 @c End:
diff --git a/libguile/debug.c b/libguile/debug.c
index 88a01d6..97b5bef 100644
--- a/libguile/debug.c
+++ b/libguile/debug.c
@@ -208,6 +208,17 @@ SCM_DEFINE (scm_debug_hang, "debug-hang", 0, 1, 0,
 #undef FUNC_NAME
 #endif
 
+SCM
+scm_local_eval (SCM exp, SCM env)
+{
+  static SCM local_eval_var = SCM_BOOL_F;
+
+  if (scm_is_false (local_eval_var))
+    local_eval_var = scm_c_module_lookup
+      (scm_c_resolve_module ("ice-9 local-eval"), "local-eval");
+  return scm_call_2 (SCM_VARIABLE_REF (local_eval_var), exp, env);
+}
+
 static void
 init_stack_limit (void)
 {
diff --git a/libguile/debug.h b/libguile/debug.h
index d862aba..8bbf88f 100644
--- a/libguile/debug.h
+++ b/libguile/debug.h
@@ -41,6 +41,8 @@ typedef union scm_t_debug_info
 
 
 
+SCM_API SCM scm_local_eval (SCM exp, SCM env);
+
 SCM_API SCM scm_reverse_lookup (SCM env, SCM data);
 SCM_API SCM scm_procedure_source (SCM proc);
 SCM_API SCM scm_procedure_name (SCM proc);
diff --git a/module/Makefile.am b/module/Makefile.am
index 56fa48d..d25842d 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -243,7 +243,8 @@ ICE_9_SOURCES = \
   ice-9/weak-vector.scm \
   ice-9/list.scm \
   ice-9/serialize.scm \
-  ice-9/vlist.scm
+  ice-9/vlist.scm \
+  ice-9/local-eval.scm
 
 SRFI_SOURCES = \
   srfi/srfi-1.scm \
diff --git a/module/ice-9/local-eval.scm b/module/ice-9/local-eval.scm
new file mode 100644
index 0000000..749c095
--- /dev/null
+++ b/module/ice-9/local-eval.scm
@@ -0,0 +1,110 @@
+;;; -*- mode: scheme; coding: utf-8; -*-
+;;;
+;;; Copyright (C) 2012 Free Software Foundation, Inc.
+;;;
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
USA
+
+(define-module (ice-9 local-eval)
+  #:use-module (ice-9 format)
+  #:use-module (srfi srfi-9)
+  #:use-module (srfi srfi-9 gnu)
+  #:use-module (system base compile)
+  #:export (local-eval local-compile))
+
+(define-record-type lexical-environment-type
+  (make-lexical-environment wrapper names boxes others module-name)
+  lexical-environment?
+  (wrapper     lexenv-wrapper)
+  (names       lexenv-names)
+  (boxes       lexenv-boxes)
+  (others      lexenv-others)
+  (module-name lexenv-module-name))
+
+(set-record-type-printer!
+ lexical-environment-type
+ (lambda (e port)
+   (format port "#<lexical-environment ~S ~S ~S>"
+          (lexenv-module-name e)
+          (map (lambda (name box) (list name (box)))
+               (lexenv-names e) (lexenv-boxes e))
+          (lexenv-others e))))
+
+(define (local-eval x e)
+  (cond ((lexical-environment? e)
+        (apply (eval ((lexenv-wrapper e) x)
+                     (resolve-module (lexenv-module-name e)))
+               (lexenv-boxes e)))
+       ((module? e) (eval x e))
+       (else (error "local-eval: invalid lexical environment" e))))
+
+(define* (local-compile x e #:key (to 'value) (opts '()))
+  (cond ((lexical-environment? e)
+        (apply (compile ((lexenv-wrapper e) x)
+                        #:env (resolve-module (lexenv-module-name e))
+                        #:from 'scheme #:to to #:opts opts)
+               (lexenv-boxes e)))
+       ((module? e) (compile x #:env e #:from 'scheme #:to to #:opts opts))
+       (else (error "local-compile: invalid lexical environment" e))))
+
+(define-syntax-rule (box v)
+  (case-lambda
+   (() v)
+   ((x) (set! v x))))
+
+(define-syntax-rule (box-lambda* (v ...) (other ...) e)
+  (lambda (v ...)
+    (let-syntax
+       ((v (identifier-syntax-from-box v))
+        ...
+        (other (unsupported-binding 'other))
+        ...)
+      (if #t e))))
+
+(define-syntax-rule (capture-environment
+                    module-name (v ...) (b ...) (other ...))
+  (make-lexical-environment
+   (lambda (expression)
+     #`(box-lambda*
+       #,'(v ...)
+       #,'(other ...)
+       #,expression))
+   '(v ...)
+   (list b ...)
+   '(other ...)
+   'module-name))
+
+(define-syntax-rule (identifier-syntax-from-box b)
+  (let ((trans (identifier-syntax
+               (id          (b))
+               ((set! id x) (b x)))))
+    (set-procedure-property! trans
+                            'identifier-syntax-box
+                            (syntax-object-of b))
+    trans))
+
+;; XXX The returned syntax object includes an anti-mark
+;;     Is there a good way to avoid this?
+(define-syntax syntax-object-of
+  (lambda (form)
+    (syntax-case form ()
+      ((_ x) #`(quote #,(datum->syntax #'x #'x))))))
+
+(define (unsupported-binding name)
+  (make-variable-transformer
+   (lambda (x)
+     (syntax-violation
+      name
+      "unsupported binding captured by (the-environment)"
+      x))))
diff --git a/module/ice-9/psyntax.scm b/module/ice-9/psyntax.scm
index 4fec917..9393e2c 100644
--- a/module/ice-9/psyntax.scm
+++ b/module/ice-9/psyntax.scm
@@ -784,6 +784,55 @@
                       id))))))
          (else (syntax-violation 'id-var-name "invalid id" id)))))
 
+    ;;
+    ;; reachable-sym+labels returns an alist containing one entry
+    ;; (sym . label) for each binding that is accessible using normal
+    ;; symbols.
+    ;;
+    ;; This implementation was derived from that of id-var-name (above),
+    ;; and closely mirrors its structure.
+    ;;
+    (define reachable-sym+labels
+      (lambda (w)
+        (define scan
+          (lambda (subst marks results)
+            (if (null? subst)
+                results
+                (let ((fst (car subst)))
+                  (if (eq? fst 'shift)
+                      (scan (cdr subst) (cdr marks) results)
+                      (let ((symnames (ribcage-symnames fst)))
+                        (if (vector? symnames)
+                            (scan-vector-rib subst marks symnames fst results)
+                            (scan-list-rib subst marks symnames fst 
results))))))))
+        (define scan-list-rib
+          (lambda (subst marks symnames ribcage results)
+            (let f ((symnames symnames) (i 0) (results results))
+              (cond
+               ((null? symnames) (scan (cdr subst) marks results))
+               ((and (not (assq (car symnames) results))
+                     (same-marks? marks (list-ref (ribcage-marks ribcage) i)))
+                (f (cdr symnames)
+                   (fx+ i 1)
+                   (cons (cons (car symnames)
+                               (list-ref (ribcage-labels ribcage) i))
+                         results)))
+               (else (f (cdr symnames) (fx+ i 1) results))))))
+        (define scan-vector-rib
+          (lambda (subst marks symnames ribcage results)
+            (let ((n (vector-length symnames)))
+              (let f ((i 0) (results results))
+                (cond
+                 ((fx= i n) (scan (cdr subst) marks results))
+                 ((and (not (assq (vector-ref symnames i) results))
+                       (same-marks? marks (vector-ref (ribcage-marks ribcage) 
i)))
+                  (f (fx+ i 1)
+                     (cons (cons (vector-ref symnames i)
+                                 (vector-ref (ribcage-labels ribcage) i))
+                           results)))
+                 (else (f (fx+ i 1) results)))))))
+        (scan (wrap-subst w) (wrap-marks w) '())))
+
     ;; free-id=? must be passed fully wrapped ids since (free-id=? x y)
     ;; may be true even if (free-id=? (wrap x w) (wrap y w)) is not.
 
@@ -1791,6 +1840,71 @@
                        (_ (syntax-violation 'quote "bad syntax"
                                             (source-wrap e w s mod))))))
 
+    (global-extend 'core 'the-environment
+                   (lambda (e r w s mod)
+                     (define local-eval-wrap
+                       (lambda (sym)
+                         (wrap sym top-wrap '(private ice-9 local-eval))))
+                     (define remove-anti-mark
+                       (lambda (id)
+                         (let ((w (syntax-object-wrap id)))
+                           (if (eq? the-anti-mark (car (wrap-marks w)))
+                               (make-syntax-object
+                                (syntax-object-expression id)
+                                (make-wrap (cdr (wrap-marks w))
+                                           (cdr (wrap-subst w)))
+                                (syntax-object-module id))
+                               id))))
+                     (define gen-capture-params
+                       (lambda ()
+                         (let loop ((sym+labels (reachable-sym+labels w))
+                                    (vars '()) (boxes '()) (others '()))
+                           (if (null? sym+labels)
+                               (values vars boxes others)
+                               (let* ((id   (wrap   (caar sym+labels) w mod))
+                                      (b    (lookup (cdar sym+labels) r mod))
+                                      (type (binding-type b)))
+                                   (cond
+                                    ((eq? type 'lexical)
+                                     (loop (cdr sym+labels)
+                                           (cons id vars)
+                                           (cons `(,(local-eval-wrap 'box) ,id)
+                                                 boxes)
+                                           others))
+                                    ((and (eq? type 'macro)
+                                          (procedure-property (binding-value b)
+                                                              
'identifier-syntax-box))
+                                     => (lambda (box)
+                                          (loop (cdr sym+labels)
+                                                (cons id vars)
+                                                (cons (remove-anti-mark box) 
boxes)
+                                                others)))
+                                    ;;
+                                    ;; ENHANCE-ME: Handle more types of local 
macros.
+                                    ;; At the very least, it should be 
possible to handle
+                                    ;; local syntax-rules macros, by saving 
the macro body
+                                    ;; in a procedure-property of the 
transformer, and
+                                    ;; then wrapping the locally-evaluated 
expression
+                                    ;; with an equivalent set of nested 
let-syntax forms
+                                    ;; (replacing the current flat let-syntax 
generated
+                                    ;; by box-lambda*).  In practice, most 
syntax-case
+                                    ;; macros could be handled this way too, 
although
+                                    ;; the emulation would not be perfect, 
e.g. in cases
+                                    ;; when the transformer contains local 
state.
+                                    ;;
+                                    (else (loop (cdr sym+labels)
+                                                vars boxes (cons id 
others)))))))))
+                     (syntax-case e ()
+                       ((_)
+                        (call-with-values
+                            (lambda () (gen-capture-params))
+                          (lambda (vars boxes others)
+                            (expand `(,(local-eval-wrap 'capture-environment)
+                                      ,(cdr mod) ,vars ,boxes ,others)
+                                    r empty-wrap mod))))
+                       (_ (syntax-violation 'the-environment "bad syntax"
+                                            (source-wrap e w s mod))))))
+
     (global-extend 'core 'syntax
                    (let ()
                      (define gen-syntax
diff --git a/test-suite/standalone/test-loose-ends.c 
b/test-suite/standalone/test-loose-ends.c
index 2fdbe7d..02da9a2 100644
--- a/test-suite/standalone/test-loose-ends.c
+++ b/test-suite/standalone/test-loose-ends.c
@@ -43,9 +43,23 @@ test_scm_from_locale_keywordn ()
 }
 
 static void
+test_scm_local_eval ()
+{
+  SCM result = scm_local_eval
+    (scm_list_3 (scm_from_latin1_symbol ("+"),
+                scm_from_latin1_symbol ("x"),
+                scm_from_latin1_symbol ("y")),
+     scm_c_eval_string ("(let ((x 1) (y 2)) (the-environment))"));
+     
+  assert (scm_is_true (scm_equal_p (result,
+                                   scm_from_signed_integer (3))));
+}
+
+static void
 tests (void *data, int argc, char **argv)
 {
   test_scm_from_locale_keywordn ();
+  test_scm_local_eval ();
 }
 
 int
diff --git a/test-suite/tests/eval.test b/test-suite/tests/eval.test
index a128cd7..91589f0 100644
--- a/test-suite/tests/eval.test
+++ b/test-suite/tests/eval.test
@@ -19,7 +19,8 @@
   :use-module (test-suite lib)
   :use-module ((srfi srfi-1) :select (unfold count))
   :use-module ((system vm vm) :select (make-vm call-with-vm))
-  :use-module (ice-9 documentation))
+  :use-module (ice-9 documentation)
+  :use-module (ice-9 local-eval))
 
 
 (define exception:bad-expression
@@ -422,4 +423,67 @@
           (thunk (let loop () (cons 's (loop)))))
       (call-with-vm vm thunk))))
 
+;;;
+;;; local-eval
+;;;
+
+(with-test-prefix "local evaluation"
+
+  (pass-if "local-eval"
+
+    (let* ((env1 (let ((x 1) (y 2) (z 3))
+                   (define-syntax-rule (foo x) (quote x))
+                   (the-environment)))
+           (env2 (local-eval '(let ((x 111) (a 'a))
+                                (define-syntax-rule (bar x) (quote x))
+                                (the-environment))
+                           env1)))
+      (local-eval '(set! x 11) env1)
+      (local-eval '(set! y 22) env1)
+      (local-eval '(set! z 33) env2)
+      (and (equal? (local-eval '(list x y z) env1)
+                   '(11 22 33))
+           (equal? (local-eval '(list x y z a) env2)
+                   '(111 22 33 a)))))
+
+  (pass-if "local-compile"
+
+    (let* ((env1 (let ((x 1) (y 2) (z 3))
+                   (define-syntax-rule (foo x) (quote x))
+                   (the-environment)))
+           (env2 (local-compile '(let ((x 111) (a 'a))
+                                   (define-syntax-rule (bar x) (quote x))
+                                   (the-environment))
+                                env1)))
+      (local-compile '(set! x 11) env1)
+      (local-compile '(set! y 22) env1)
+      (local-compile '(set! z 33) env2)
+      (and (equal? (local-compile '(list x y z) env1)
+                   '(11 22 33))
+           (equal? (local-compile '(list x y z a) env2)
+                   '(111 22 33 a)))))
+
+  (pass-if "mixed primitive-eval, local-eval and local-compile"
+
+    (let* ((env1 (primitive-eval '(let ((x 1) (y 2) (z 3))
+                                    (define-syntax-rule (foo x) (quote x))
+                                    (the-environment))))
+           (env2 (local-eval '(let ((x 111) (a 'a))
+                                (define-syntax-rule (bar x) (quote x))
+                                (the-environment))
+                             env1))
+           (env3 (local-compile '(let ((y 222) (b 'b))
+                                   (the-environment))
+                                env2)))
+      (local-eval    '(set! x 11) env1)
+      (local-compile '(set! y 22) env2)
+      (local-eval    '(set! z 33) env2)
+      (local-compile '(set! a (* y 2)) env3)
+      (and (equal? (local-compile '(list x y z) env1)
+                   '(11 22 33))
+           (equal? (local-eval '(list x y z a) env2)
+                   '(111 22 33 444))
+           (equal? (local-eval '(list x y z a b) env3)
+                   '(111 222 33 444 b))))))
+
 ;;; eval.test ends here
-- 
1.7.5.4


reply via email to

[Prev in Thread] Current Thread [Next in Thread]