Hi all,

While talking to Demosthenex in #chicken, I decided it would be cool
to compare the performance of the "json" egg in CHICKEN 5 with that
in CHICKEN 4.  However, I got some errors when building the egg,
which have to do with the way the "packrat" egg expands its bodies.

It turns out that packrat-parser expands into this sort of pattern:

(let ()
  (define nonterminal1 (lambda (results) ...)) 
  (define nonterminal2 (lambda (results) ...)) 
  body)

And in json.scm, the json-read parser uses something like

(packrat-parser (begin (define (helper1 results) ...)
                       (define (helper2 results) ...)
                       the-parser)
                (the-parser ((helper1 ...))))

So the final output is like

(let ()
  (define parse (lambda (results) (helper1 ..))) 
  (begin (define (helper1 results) ...)
         (define (helper2 results) ...)
         parse))

And if the begin starts a new letrec, this causes the helpers
to be unavailable from "parse".

Now, the spec disallows this (I think..), but we typically treat
begin as a sort of no-op, so in the above example, all 3 defines
should be in one letrec sequence.  I also noticed that if we
create a macro that's basically an alias for define, it starts
a new letrec.

This is all due a simplification I added in c9220247d that I thought
shouldn't cause problems: I removed the macro-expansion in the main
loop of canonicalize-body because "there already was one in the
secondary loop" (and the check for the macro being a member of
"vars" seemed a bit iffy to me as well).  Technically that's true,
but I hadn't thought through the implications of this resulting in
a new letrec.

So attached is a relatively simple patch which restores the old macro
expansion behaviour in canonicalize-body, plus two regression tests
for these situations.

PS: It turns out the json egg was slightly slower to parse the benchmark
file with CHICKEN 5 than with CHICKEN 4 but it used less memory.

Cheers,
Peter
From 144b11ecc6b1d5984be7a99e3141b3af9bce9869 Mon Sep 17 00:00:00 2001
From: Peter Bex <pe...@more-magic.net>
Date: Thu, 27 Apr 2017 17:43:13 +0200
Subject: [PATCH] Restore macro-expansion in canonicalize-body's "main" loop

In commit c9220247dbcdf6fd39697b428cfd40068244219a, we removed a
little bit too much: the original code's expansion in the main loop
would expand begins and defines at the root level, which means they'd
be "flattened" into the same letrec.

This broke a few situations that are technically somewhat iffy, like
  (let () (define (foo) bar) (begin (define (bar) 1) (foo)))
which would error out instead of returning 1, because the begin would
stop the main loop and inject a new letrec, acting as a barrier, which
means later definitions are in a new scope that the earlier
definitions would not be able to see.

Also, if there's an macro that expands to "define", that would also be
put in its own letrec because macro-expansion would stop the current
collection of vars and mvars, much like the aforementioned begin.
---
 expand.scm             | 11 ++++++++++-
 tests/syntax-tests.scm |  6 ++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/expand.scm b/expand.scm
index 3a6b436..472d3ca 100644
--- a/expand.scm
+++ b/expand.scm
@@ -630,7 +630,16 @@
 		     (loop rest (cons (cadr x) vars) (cons (caddr x) vals) (cons #t mvars)))
 		    ((comp '##core#begin head)
 		     (loop (##sys#append (cdr x) rest) vars vals mvars))
-		    (else (fini vars vals mvars body))))))))
+		    (else
+		     ;; Do not macro-expand local definitions we are
+		     ;; in the process of introducing.
+		     (if (member (list head) vars)
+			 (fini vars vals mvars body)
+			 (let ((x2 (##sys#expand-0 x se cs?)))
+			   (if (eq? x x2)
+			       (fini vars vals mvars body)
+			       (loop (cons x2 rest)
+				     vars vals mvars)))))))))))
     (expand body) ) )
 
 
diff --git a/tests/syntax-tests.scm b/tests/syntax-tests.scm
index a803220..1c4941a 100644
--- a/tests/syntax-tests.scm
+++ b/tests/syntax-tests.scm
@@ -797,6 +797,12 @@
 
 ;; Nested begins inside definitions were not treated correctly
 (t 3 (eval '(let () (begin 1 (begin 2 (define internal-def 3) internal-def)))))
+;; Macros that expand to "define" should not cause a letrec barrier
+(t 1 (eval '(let-syntax ((my-define (syntax-rules ()
+				      ((_ var val) (define var val)))))
+	      (let () (define (run-it) foo) (my-define foo 1) (run-it)))))
+;; Begin should not cause a letrec barrier
+(t 1 (eval '(let () (define (run-it) foo) (begin (define foo 1) (run-it)))))
 (f (eval '(let () internal-def)))
 
 ;;; renaming of keyword argument (#277)
-- 
2.1.4

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Chicken-hackers mailing list
Chicken-hackers@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-hackers

Reply via email to