[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] tree-il->scheme: avoid gensyms and begin; use cond, and, or, and
From: |
Mark H Weaver |
Subject: |
[PATCH] tree-il->scheme: avoid gensyms and begin; use cond, and, or, and let* |
Date: |
Wed, 22 Feb 2012 21:50:09 -0500 |
Hello all,
Here's a preliminary patch that greatly improves our 'tree-il->scheme'
decompiler. With this patch, psyntax-pp.scm is now less than half of
its previous size (over 800 kilobytes saved), and is _far_ more
readable. In almost all cases the original source identifiers are used
instead of gensyms, while adding minimal suffixes to the least-used
variables where needed to avoid unintended variable capture. The
derived syntactic forms 'cond', 'and', 'or', and 'let*' are now
generated when appropriate, and 'begin' is no longer inserted in
contexts that provide an implicit 'begin'.
I've also disabled the use of partial evaluation when generating
psyntax-pp.scm. This is by far the biggest improvement in the size and
readability of psyntax-pp.scm, since it avoids the aggressive inlining.
Peval will still be applied when it's compiled to a .go file.
Again, this patch is preliminary, but it seems to work very well for me.
Comments and suggestions solicited.
Mark
>From 7edbbdfa277f2449e022e5d549d6a6bfb7504389 Mon Sep 17 00:00:00 2001
From: Mark H Weaver <address@hidden>
Date: Wed, 22 Feb 2012 21:11:53 -0500
Subject: [PATCH] tree-il->scheme: avoid gensyms and begin; use cond, and, or,
and let*
* module/language/tree-il.scm (choose-output-names): New internal
procedure.
(tree-il->scheme): Print source identifiers where possible, otherwise
use minimal numeric suffixes. Previously we printed the gensyms.
Avoid 'begin' in contexts that provide an implicit 'begin'. Produce
'cond', 'and', 'or', and 'let*' where appropriate. Add keyword
arguments to disable the production of these derived syntactic forms,
either globally or only within top-level definitions (a hack for use
in bootstrapping psyntax).
* module/ice-9/compile-psyntax.scm: Disable partial evaluation when
producing psyntax-pp.scm, in order to limit code growth and
obfuscation due to procedure inlining. Pass #:booting-psyntax #t
keyword argument to 'tree-il->scheme'. Pretty-print using a width
of 120 characters.
* module/ice-9/psyntax-pp.scm: Regenerate. It is now less than half of
its previous size!
---
module/ice-9/compile-psyntax.scm | 5 +-
module/ice-9/psyntax-pp.scm |37906 ++++++++++++--------------------------
module/language/tree-il.scm | 617 +-
3 files changed, 12769 insertions(+), 25759 deletions(-)
diff --git a/module/ice-9/compile-psyntax.scm b/module/ice-9/compile-psyntax.scm
index 3d803e9..d7572e4 100644
--- a/module/ice-9/compile-psyntax.scm
+++ b/module/ice-9/compile-psyntax.scm
@@ -44,8 +44,9 @@
(optimize!
(macroexpand x 'c '(compile load eval))
(current-module)
- '())))
- out)
+ '(#:partial-eval? #f)))
+ #:booting-psyntax? #t)
+ out #:width 120)
(newline out)
(loop (read in))))))
(system (format #f "mv -f ~s.tmp ~s" target target)))
diff --git a/module/language/tree-il.scm b/module/language/tree-il.scm
index 1d391c4..407bed4 100644
--- a/module/language/tree-il.scm
+++ b/module/language/tree-il.scm
@@ -1,4 +1,4 @@
-;;;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+;;;; Copyright (C) 2009, 2010, 2011, 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
@@ -19,6 +19,9 @@
(define-module (language tree-il)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-26)
+ #:use-module (ice-9 receive)
+ #:use-module (ice-9 vlist)
#:use-module (system base pmatch)
#:use-module (system base syntax)
#:export (tree-il-src
@@ -331,155 +334,244 @@
`(abort ,(unparse-tree-il tag) ,(map unparse-tree-il args)
,(unparse-tree-il tail)))))
-(define (tree-il->scheme e)
- (record-case e
- ((<void>)
- '(if #f #f))
-
- ((<application> proc args)
- `(,(tree-il->scheme proc) ,@(map tree-il->scheme args)))
-
- ((<conditional> test consequent alternate)
- (if (void? alternate)
- `(if ,(tree-il->scheme test) ,(tree-il->scheme consequent))
- `(if ,(tree-il->scheme test) ,(tree-il->scheme consequent)
,(tree-il->scheme alternate))))
-
- ((<primitive-ref> name)
- name)
-
- ((<lexical-ref> gensym)
- gensym)
-
- ((<lexical-set> gensym exp)
- `(set! ,gensym ,(tree-il->scheme exp)))
+(define* (tree-il->scheme
+ e #:key (use-derived-syntax? #t) (booting-psyntax? #f))
+
+ (receive (output-name-table occurrence-count-table)
+ (choose-output-names e use-derived-syntax?)
+
+ (define (output-name s) (hashq-ref output-name-table s))
+ (define (occurrence-count s) (hashq-ref occurrence-count-table s))
+
+ (define (false? e)
+ (and (const? e) (eq? #f (const-exp e))))
+ (define (lex-var? gensym e)
+ (and (lexical-ref? e)
+ (eq? gensym (lexical-ref-gensym e))))
+ (define (let-1? e)
+ (and (let? e) (= 1 (length (let-gensyms e)))))
+ (define (or-expr? e)
+ (and (let-1? e)
+ (let ((t (car (let-gensyms e)))
+ (c (let-body e)))
+ (and (conditional? c)
+ (lex-var? t (conditional-test c))
+ (lex-var? t (conditional-consequent c))
+ (= 3 (occurrence-count t))))))
+
+ (let recurse-with-options ((e e) (use-derived-syntax? use-derived-syntax?))
+ (let recurse ((e e))
+ (define (recurse-body e)
+ (map recurse (if (sequence? e)
+ (sequence-exps e)
+ (list e))))
+
+ (record-case e
+ ((<void>)
+ '(if #f #f))
+
+ ((<const> exp)
+ (if (and (self-evaluating? exp) (not (vector? exp)))
+ exp
+ (list 'quote exp)))
- ((<module-ref> mod name public?)
- `(,(if public? '@ '@@) ,mod ,name))
-
- ((<module-set> mod name public? exp)
- `(set! (,(if public? '@ '@@) ,mod ,name) ,(tree-il->scheme exp)))
-
- ((<toplevel-ref> name)
- name)
-
- ((<toplevel-set> name exp)
- `(set! ,name ,(tree-il->scheme exp)))
-
- ((<toplevel-define> name exp)
- `(define ,name ,(tree-il->scheme exp)))
-
- ((<lambda> meta body)
- ;; fixme: put in docstring
- (tree-il->scheme body))
+ ((<sequence> exps)
+ `(begin ,@(map recurse exps)))
- ((<lambda-case> req opt rest kw inits gensyms body alternate)
- (cond
- ((and (not opt) (not kw) (not alternate))
- `(lambda ,(if rest (apply cons* gensyms) gensyms)
- ,(tree-il->scheme body)))
- ((and (not opt) (not kw))
- (let ((alt-expansion (tree-il->scheme alternate))
- (formals (if rest (apply cons* gensyms) gensyms)))
- (case (car alt-expansion)
- ((lambda)
- `(case-lambda (,formals ,(tree-il->scheme body))
- ,(cdr alt-expansion)))
- ((lambda*)
- `(case-lambda* (,formals ,(tree-il->scheme body))
- ,(cdr alt-expansion)))
- ((case-lambda)
- `(case-lambda (,formals ,(tree-il->scheme body))
- ,@(cdr alt-expansion)))
- ((case-lambda*)
- `(case-lambda* (,formals ,(tree-il->scheme body))
- ,@(cdr alt-expansion))))))
- (else
- (let* ((alt-expansion (and alternate (tree-il->scheme alternate)))
- (nreq (length req))
- (nopt (if opt (length opt) 0))
- (restargs (if rest (list-ref gensyms (+ nreq nopt)) '()))
- (reqargs (list-head gensyms nreq))
- (optargs (if opt
- `(#:optional
- ,@(map list
- (list-head (list-tail gensyms nreq) nopt)
- (map tree-il->scheme
- (list-head inits nopt))))
- '()))
- (kwargs (if kw
- `(#:key
- ,@(map list
- (map caddr (cdr kw))
- (map tree-il->scheme
- (list-tail inits nopt))
- (map car (cdr kw)))
- ,@(if (car kw)
- '(#:allow-other-keys)
+ ((<application> proc args)
+ `(,(recurse proc) ,@(map recurse args)))
+
+ ((<primitive-ref> name)
+ name)
+
+ ((<lexical-ref> gensym)
+ (output-name gensym))
+
+ ((<lexical-set> gensym exp)
+ `(set! ,(output-name gensym) ,(recurse exp)))
+
+ ((<module-ref> mod name public?)
+ `(,(if public? '@ '@@) ,mod ,name))
+
+ ((<module-set> mod name public? exp)
+ `(set! (,(if public? '@ '@@) ,mod ,name) ,(recurse exp)))
+
+ ((<toplevel-ref> name)
+ name)
+
+ ((<toplevel-set> name exp)
+ `(set! ,name ,(recurse exp)))
+
+ ((<toplevel-define> name exp)
+ `(define ,name ,(if booting-psyntax?
+ (recurse-with-options exp #f)
+ (recurse exp))))
+
+ ((<lambda> meta body)
+ ;; FIXME: include the docstring
+ (recurse body))
+
+ ((<lambda-case> req opt rest kw inits gensyms body alternate)
+ (let ((names (map output-name gensyms)))
+ (cond
+ ((and (not opt) (not kw) (not alternate))
+ `(lambda ,(if rest (apply cons* names) names)
+ ,@(recurse-body body)))
+ ((and (not opt) (not kw))
+ (let ((alt-expansion (recurse alternate))
+ (formals (if rest (apply cons* names) names)))
+ (case (car alt-expansion)
+ ((lambda)
+ `(case-lambda (,formals ,@(recurse-body body))
+ ,(cdr alt-expansion)))
+ ((lambda*)
+ `(case-lambda* (,formals ,@(recurse-body body))
+ ,(cdr alt-expansion)))
+ ((case-lambda)
+ `(case-lambda (,formals ,@(recurse-body body))
+ ,@(cdr alt-expansion)))
+ ((case-lambda*)
+ `(case-lambda* (,formals ,@(recurse-body body))
+ ,@(cdr alt-expansion))))))
+ (else
+ (let* ((alt-expansion (and alternate (recurse alternate)))
+ (nreq (length req))
+ (nopt (if opt (length opt) 0))
+ (restargs (if rest (list-ref names (+ nreq nopt)) '()))
+ (reqargs (list-head names nreq))
+ (optargs (if opt
+ `(#:optional
+ ,@(map list
+ (list-head (list-tail names nreq)
nopt)
+ (map recurse
+ (list-head inits nopt))))
+ '()))
+ (kwargs (if kw
+ `(#:key
+ ,@(map list
+ (map output-name (map caddr (cdr
kw)))
+ (map recurse
+ (list-tail inits nopt))
+ (map car (cdr kw)))
+ ,@(if (car kw)
+ '(#:allow-other-keys)
+ '()))
'()))
- '()))
- (formals `(,@reqargs ,@optargs ,@kwargs . ,restargs)))
- (if (not alt-expansion)
- `(lambda* ,formals ,(tree-il->scheme body))
- (case (car alt-expansion)
- ((lambda lambda*)
- `(case-lambda* (,formals ,(tree-il->scheme body))
- ,(cdr alt-expansion)))
- ((case-lambda case-lambda*)
- `(case-lambda* (,formals ,(tree-il->scheme body))
- ,@(cdr alt-expansion)))))))))
+ (formals `(,@reqargs ,@optargs ,@kwargs . ,restargs)))
+ (if (not alt-expansion)
+ `(lambda* ,formals ,@(recurse-body body))
+ (case (car alt-expansion)
+ ((lambda lambda*)
+ `(case-lambda* (,formals ,@(recurse-body body))
+ ,(cdr alt-expansion)))
+ ((case-lambda case-lambda*)
+ `(case-lambda* (,formals ,@(recurse-body body))
+ ,@(cdr alt-expansion))))))))))
- ((<const> exp)
- (if (and (self-evaluating? exp) (not (vector? exp)))
- exp
- (list 'quote exp)))
-
- ((<sequence> exps)
- `(begin ,@(map tree-il->scheme exps)))
-
- ((<let> gensyms vals body)
- `(let ,(map list gensyms (map tree-il->scheme vals)) ,(tree-il->scheme
body)))
-
- ((<letrec> in-order? gensyms vals body)
- `(,(if in-order? 'letrec* 'letrec)
- ,(map list gensyms (map tree-il->scheme vals)) ,(tree-il->scheme body)))
-
- ((<fix> gensyms vals body)
- ;; not a typo, we really do translate back to letrec. use letrec* since it
- ;; doesn't matter, and the naive letrec* transformation does not require
an
- ;; inner let.
- `(letrec* ,(map list gensyms (map tree-il->scheme vals))
,(tree-il->scheme body)))
+ ((<conditional> test consequent alternate)
+ (cond ((and use-derived-syntax? (false? alternate))
+ (let loop ((exps (list test))
+ (consequent consequent))
+ (if (and (conditional? consequent)
+ (false? (conditional-alternate consequent)))
+ (loop (cons (conditional-test consequent) exps)
+ (conditional-consequent consequent))
+ `(and ,@(reverse! (map recurse exps))
+ ,(recurse consequent)))))
+ ((and use-derived-syntax? (conditional? alternate))
+ (let loop ((tests (list test))
+ (bodies (list (recurse-body consequent)))
+ (rest alternate))
+ (cond ((conditional? rest)
+ (loop (cons (conditional-test rest) tests)
+ (cons (recurse-body
+ (conditional-consequent rest))
+ bodies)
+ (conditional-alternate rest)))
+ ((or-expr? rest)
+ (loop (append (let-vals rest) tests)
+ (cons '() bodies)
+ (conditional-alternate (let-body rest))))
+ (else
+ `(cond ,@(reverse!
+ (append (if (void? rest)
+ '()
+ `((else ,@(recurse-body
rest))))
+ (map cons
+ (map recurse tests)
+ bodies))))))))
+ ((void? alternate)
+ `(if ,(recurse test) ,(recurse consequent)))
+ (else
+ `(if ,(recurse test) ,(recurse consequent)
+ ,(recurse alternate)))))
+
+ ((<let> gensyms vals body)
+ (cond ((and use-derived-syntax?
+ (or-expr? e))
+ (let loop ((vals vals) (body (conditional-alternate body)))
+ (if (or-expr? body)
+ (loop (append (let-vals body) vals)
+ (conditional-alternate (let-body body)))
+ `(or ,@(reverse! (map recurse vals))
+ ,(recurse body)))))
+ ((and use-derived-syntax? (let-1? e) (let-1? body))
+ (let loop ((gensyms gensyms) (vals vals) (body body))
+ (if (and (let-1? body) (not (or-expr? body)))
+ (loop (append (let-gensyms body) gensyms)
+ (append (let-vals body) vals)
+ (let-body body))
+ `(let* ,(reverse! (map list
+ (map output-name gensyms)
+ (map recurse vals)))
+ ,@(recurse-body body)))))
+ (else
+ `(let ,(map list (map output-name gensyms) (map recurse
vals))
+ ,@(recurse-body body)))))
+
+ ((<letrec> in-order? gensyms vals body)
+ `(,(if in-order? 'letrec* 'letrec)
+ ,(map list (map output-name gensyms) (map recurse vals))
+ ,@(recurse-body body)))
+
+ ((<fix> gensyms vals body)
+ ;; not a typo, we really do translate back to letrec. use letrec*
since it
+ ;; doesn't matter, and the naive letrec* transformation does not
require an
+ ;; inner let.
+ `(letrec* ,(map list (map output-name gensyms) (map recurse vals))
+ ,@(recurse-body body)))
- ((<let-values> exp body)
- `(call-with-values (lambda () ,(tree-il->scheme exp))
- ,(tree-il->scheme (make-lambda #f '() body))))
+ ((<let-values> exp body)
+ `(call-with-values (lambda () ,@(recurse-body exp))
+ ,(recurse (make-lambda #f '() body))))
- ((<dynwind> body winder unwinder)
- `(dynamic-wind ,(tree-il->scheme winder)
- (lambda () ,(tree-il->scheme body))
- ,(tree-il->scheme unwinder)))
+ ((<dynwind> body winder unwinder)
+ `(dynamic-wind ,(recurse winder)
+ (lambda () ,@(recurse-body body))
+ ,(recurse unwinder)))
- ((<dynlet> fluids vals body)
- `(with-fluids ,(map list
- (map tree-il->scheme fluids)
- (map tree-il->scheme vals))
- ,(tree-il->scheme body)))
+ ((<dynlet> fluids vals body)
+ `(with-fluids ,(map list
+ (map recurse fluids)
+ (map recurse vals))
+ ,@(recurse-body body)))
- ((<dynref> fluid)
- `(fluid-ref ,(tree-il->scheme fluid)))
+ ((<dynref> fluid)
+ `(fluid-ref ,(recurse fluid)))
- ((<dynset> fluid exp)
- `(fluid-set! ,(tree-il->scheme fluid) ,(tree-il->scheme exp)))
+ ((<dynset> fluid exp)
+ `(fluid-set! ,(recurse fluid) ,(recurse exp)))
- ((<prompt> tag body handler)
- `(call-with-prompt
- ,(tree-il->scheme tag)
- (lambda () ,(tree-il->scheme body))
- ,(tree-il->scheme handler)))
+ ((<prompt> tag body handler)
+ `(call-with-prompt
+ ,(recurse tag)
+ (lambda () ,@(recurse-body body))
+ ,(recurse handler)))
- ((<abort> tag args tail)
- `(apply abort ,(tree-il->scheme tag) ,@(map tree-il->scheme args)
- ,(tree-il->scheme tail)))))
+ ((<abort> tag args tail)
+ `(apply abort ,(recurse tag) ,@(map recurse args)
+ ,(recurse tail))))))))
(define (tree-il-fold leaf down up seed tree)
@@ -792,3 +884,252 @@ This is an implementation of `foldts' as described by
Andy Wingo in
(else #f))
x)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Algorithm for choosing better variable names
+;; ============================================
+;;
+;; First we perform an analysis pass, collecting the following
+;; information:
+;;
+;; * For each gensym: how many occurrences will occur in the output?
+;;
+;; * For each gensym A: which gensyms does A conflict with? Gensym A and
+;; gensym B conflict if they have the same source name, and if giving
+;; them the same name would cause a bad variable reference due to
+;; unintentional variable capture.
+;;
+;; The occurrence counter is indexed by gensym and is global (within each
+;; invocation of the algorithm), implemented using a hash table. We also
+;; keep a global mapping from gensym to source name as provided by the
+;; binding construct (we prefer not to trust the source names in the
+;; lexical ref or set).
+;;
+;; As we recurse down into lexical binding forms, we keep track of a
+;; mapping from source name to an ordered list of bindings, innermost
+;; first. When we encounter a variable occurrence, we increment the
+;; counter, map to source name (preferring not to trust the 'name' in
+;; the lexical ref or set), and then look up the bindings currently in
+;; effect for that source name. Hopefully our gensym will be the first
+;; (innermost) binding. If not, we register a conflict between the
+;; referenced gensym and the other bound gensyms with the same source
+;; name that shadow the one we want. These are simply the gensyms on
+;; the binding list that come before our gensym.
+;;
+;; Top-level variables are treated specially. Whenever they occur, they
+;; register a conflict with every lexical binding currently in effect
+;; with the same source name. They are guaranteed to be assigned to
+;; their source names.
+;;
+;; XXX FIXME: Currently, primitives are treated exactly like top-level
+;; bindings. This handles conflicting lexical bindings properly, but
+;; does _not_ handle the case where top-level bindings conflict with the
+;; needed primitives.
+;;
+;; Also note that this requires that 'choose-output-names' be kept in
+;; sync with 'tree-il->scheme'. Primitives that are introduced by
+;; 'tree-il->scheme' must be anticipated by 'choose-output-name'.
+;;
+;;
+;; How we assign an output name to each gensym
+;; ===========================================
+;;
+;; We process the gensyms in order of decreasing occurrence count, with
+;; each gensym choosing the best output name possible, as long as it
+;; isn't the same name as any of the previously-chosen output names of
+;; conflicting gensyms.
+;;
+
+
+;;
+;; 'choose-output-names' analyzes the top-level form e, chooses good
+;; variable names that are as close as possible to the source names,
+;; and returns two values:
+;;
+;; * a hash table mapping gensym to output name
+;; * a hash table mapping gensym to number of occurrences
+;;
+(define (choose-output-names e use-derived-syntax?)
+ (define gensyms '())
+
+ (define occurrence-count-table (make-hash-table))
+ (define (occurrence-count s) (or (hashq-ref occurrence-count-table s) 0))
+ (define (increment-occurrence-count! s)
+ (let ((h (hashq-create-handle! occurrence-count-table s 0)))
+ (if (zero? (cdr h))
+ (set! gensyms (cons s gensyms)))
+ (set-cdr! h (+ 1 (cdr h)))))
+
+ (define source-name-table (make-hash-table))
+ (define (set-source-name! s name) (hashq-set! source-name-table s name))
+ (define (source-name s)
+ (if (pair? s) (cdr s) (hashq-ref source-name-table s)))
+
+ (define conflict-table (make-hash-table))
+ (define (conflicts s) (or (hashq-ref conflict-table s) '()))
+ (define (add-conflict! a b)
+ (define (add! a b)
+ (if (not (pair? a))
+ (let ((h (hashq-create-handle! conflict-table a '())))
+ (if (not (memq b (cdr h)))
+ (set-cdr! h (cons b (cdr h)))))))
+ (add! a b)
+ (add! b a))
+
+ (define top-level-intern!
+ (let ((table (make-hash-table)))
+ (lambda (name)
+ (cdr (hashq-create-handle! table name (cons 'top-level name))))))
+
+ (let recurse-with-bindings ((e e) (bindings vlist-null))
+ (let recurse ((e e))
+
+ (define done #t)
+
+ (define (top-level name)
+ (let ((s (top-level-intern! name))
+ (conflicts (vhash-foldq* cons '() name bindings)))
+ (for-each (cut add-conflict! s <>) conflicts)))
+
+ ;; XXX FIXME: Currently, primitives are treated exactly like
+ ;; top-level bindings. This handles conflicting lexical bindings
+ ;; properly, but does _not_ handle the case where top-level
+ ;; bindings conflict with the needed primitives.
+ (define (primitive name) (top-level name))
+
+ (define (lexical s)
+ (increment-occurrence-count! s)
+ (let ((conflicts
+ (take-while (lambda (s*) (not (eq? s s*)))
+ (reverse!
+ (vhash-foldq*
+ cons '() (source-name s) bindings)))))
+ (for-each (cut add-conflict! s <>) conflicts)))
+
+ (record-case e
+ ((<void>) (primitive 'if))
+ ((<const>) done)
+
+ ((<application> proc args)
+ (for-each recurse (cons proc args)))
+
+ ((<primitive-ref> name) (primitive name))
+
+ ((<lexical-ref> gensym) (lexical gensym))
+ ((<lexical-set> gensym exp)
+ (primitive 'set!) (lexical gensym) (recurse exp))
+
+ ((<module-ref> public?) (primitive (if public? '@ '@@)))
+ ((<module-set> public? exp)
+ (primitive 'set!) (primitive (if public? '@ '@@)) (recurse exp))
+
+ ((<toplevel-ref> name) (top-level name))
+ ((<toplevel-set> name exp) (top-level name) (recurse exp))
+ ((<toplevel-define> name exp) (top-level name) (recurse exp))
+
+ ((<conditional> test consequent alternate)
+ (define (false? e) (and (const? e) (eq? #f (const-exp e))))
+ (cond ((and use-derived-syntax? (false? alternate))
+ (primitive 'and))
+ ((and use-derived-syntax? (conditional? alternate))
+ (primitive 'cond)
+ (primitive 'else)))
+ (primitive 'if)
+ (recurse test) (recurse consequent) (recurse alternate))
+
+ ((<sequence> exps) (primitive 'begin) (for-each recurse exps))
+ ((<lambda> body) (recurse body))
+
+ ((<lambda-case> req opt rest kw inits gensyms body alternate)
+ (primitive 'lambda)
+ (cond ((or opt kw alternate)
+ (primitive 'lambda*)
+ (primitive 'case-lambda)))
+ (let* ((names (append req (or opt '()) (if rest (list rest) '())
+ (map cadr (if kw (cdr kw) '()))))
+ (body-bindings (fold vhash-consq bindings names gensyms)))
+ (for-each increment-occurrence-count! gensyms)
+ (for-each set-source-name! gensyms names)
+ (for-each recurse inits)
+ (recurse-with-bindings body body-bindings)
+ (if alternate (recurse alternate))))
+
+ ((<let> names gensyms vals body)
+ (primitive 'let)
+ (cond (use-derived-syntax? (primitive 'let*) (primitive 'or)))
+ (for-each increment-occurrence-count! gensyms)
+ (for-each set-source-name! gensyms names)
+ (for-each recurse vals)
+ (recurse-with-bindings body (fold vhash-consq bindings names
gensyms)))
+
+ ((<letrec> in-order? names gensyms vals body)
+ (primitive (if in-order? 'letrec* 'letrec))
+ (for-each increment-occurrence-count! gensyms)
+ (for-each set-source-name! gensyms names)
+ (let ((bindings (fold vhash-consq bindings names gensyms)))
+ (for-each (cut recurse-with-bindings <> bindings) vals)
+ (recurse-with-bindings body bindings)))
+
+ ((<fix> names gensyms vals body)
+ (primitive 'letrec*)
+ (for-each increment-occurrence-count! gensyms)
+ (for-each set-source-name! gensyms names)
+ (let ((bindings (fold vhash-consq bindings names gensyms)))
+ (for-each (cut recurse-with-bindings <> bindings) vals)
+ (recurse-with-bindings body bindings)))
+
+ ((<let-values> exp body)
+ (primitive 'call-with-values)
+ (recurse exp) (recurse body))
+
+ ((<dynwind> winder body unwinder)
+ (primitive 'dynamic-wind)
+ (recurse winder) (recurse body) (recurse unwinder))
+
+ ((<dynlet> fluids vals body)
+ (primitive 'with-fluids)
+ (for-each recurse fluids)
+ (for-each recurse vals)
+ (recurse body))
+
+ ((<dynref> fluid) (primitive 'fluid-ref) (recurse fluid))
+ ((<dynset> fluid exp)
+ (primitive 'fluid-set!) (recurse fluid) (recurse exp))
+
+ ((<prompt> tag body handler)
+ (primitive 'call-with-prompt)
+ (primitive 'lambda)
+ (recurse tag) (recurse body) (recurse handler))
+
+ ((<abort> tag args tail)
+ (primitive 'apply)
+ (primitive 'abort)
+ (recurse tag) (for-each recurse args) (recurse tail)))))
+
+ (let ()
+ (define output-name-table (make-hash-table))
+ (define (set-output-name! s name) (hashq-set! output-name-table s name))
+ (define (output-name s)
+ (if (pair? s) (cdr s) (hashq-ref output-name-table s)))
+
+ (define sorted-gensyms
+ (sort-list gensyms (lambda (a b) (> (occurrence-count a)
+ (occurrence-count b)))))
+
+ (for-each (lambda (s)
+ (let* ((conflicts (conflicts s))
+ (sname (source-name s))
+ (prefix (string-append (symbol->string sname) "-")))
+ (let loop ((i 1) (name sname))
+ (if (any (lambda (s*)
+ (and=> (output-name s*)
+ (cut eq? name <>)))
+ conflicts)
+ (loop (+ i 1)
+ (string->symbol (string-append
+ prefix
+ (number->string i))))
+ (set-output-name! s name)))))
+ sorted-gensyms)
+ (values output-name-table occurrence-count-table)))
--
1.7.5.4
- [PATCH] tree-il->scheme: avoid gensyms and begin; use cond, and, or, and let*,
Mark H Weaver <=