guile-user
[Top][All Lists]
Advanced

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

Unbound variables used within a form


From: Panicz Maciej Godek
Subject: Unbound variables used within a form
Date: Sat, 13 Oct 2012 02:12:45 +0200

Hey,
I just wrote the biggest function in my life :)
It takes a scheme program and returns the list
of all variables (symbols) that are used but
unbound within that form. The code is pretty
straightforward:

(use-modules (srfi srfi-1)(ice-9 match)(srfi srfi-11))

(define (properize src . dst)
"Change an improper list into a proper list"
  (cond ((pair? src)
         (apply properize (cdr src) (cons (car src) dst)))
        ((null? src)
         (reverse dst))
        (else
         (reverse (cons src dst)))))

(define (used-variables form)
  (define (diff . args) (apply lset-difference equal? args))
  (define (join . args) (apply lset-union equal? args))
  (define (bound-variables bindings)
    (let-values (((names forms) (unzip2 bindings)))
      (values names (append-map used-variables forms))))
  (define (argument-name arg)
    (cond ((symbol? arg) arg)
          ((pair? arg) (car arg))
          (else #f)))
  (match form
    (((or 'let 'letrec 'letrec*) (bindings ...) body ...)
     (let-values (((shadowed used) (bound-variables bindings)))
       (join used (diff (append-map used-variables body) (diff
shadowed used)))))
    (('let (? symbol? name) (bindings ...) body ...)
     (let-values (((shadowed used) (bound-variables bindings)))
       (join used (diff (append-map used-variables body) (diff
shadowed used) (list name)))))
    (('begin body ...)
     (append-map used-variables body))
    (((or 'lambda 'lambda*) arg body ...)
     (cond
      ((or (pair? arg) (list? arg))
       (diff (append-map used-variables body) (filter-map
argument-name (properize arg))))
      ((symbol? arg)
       (diff (append-map used-variables body) (list arg)))))
    (((or 'define 'define*) (name ...) body ...)
     (diff (append-map used-variables body) name))
    (('define name value)
     (diff (used-variables value) name))
    (((or 'case-lambda 'case-lambda*) def ...)
     (apply join (map (match-lambda ((arg body)
                                     (cond
                                      ((symbol? arg)
                                       (diff (append-map used-variables body) 
(list arg)))
                                      ((or (pair? arg) (list? arg))
                                       (diff (append-map used-variables body)
                                             (filter-map argument-name 
(properize arg)))))))
                      def)))
    (((or 'if 'or 'and) expr ...)
     (append-map used-variables expr))
    (('quote data)
     '())
    (('quasiquote data)
     (letrec ((quasiquote-variables (match-lambda
                                     (('unquote data) (used-variables data))
                                     ((data ...) (append-map 
quasiquote-variables data))
                                     (else '()))))
       (quasiquote-variables data)))
    (('@@ name ...)
     '())
    ((procedure ...)
     (append-map used-variables procedure))
    ((? symbol? variable)
       (list variable))
    (else
     '())))

At least for simple cases it seems to work alright.
It has been written specifically for guile, so it took into
account various primitive language constructs that
extend RnRS (like case-lambda or lambda*).

Obviously, feel free to use the code any way you like.
I just wanted to ask:
- beside lambda*, case-lambda, case-lambda*,
are there any additional extensions that guile introduced
atop the Report that should be taken into account in this code?
- is there any simpler way to achieve the same effect, ie.
to acquire all external symbols that are meaningful within
a form (assuming the core semantics, that is, that all
the macros were expanded)

Yours,
M



reply via email to

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