From 47c8983f08157865a3937722c06acbbb3cbd7621 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Thu, 21 May 2009 13:49:00 +0200 Subject: [PATCH] rewrite `method' as a hygienic macro to re-allow lexical specializers * module/oop/goops.scm (method): Reimplement as a hygienic macro. This seriously took me like 6 hours to figure out. Allows for lexical specializers: (let (( ...)) (define-method (foo (arg )) ...)). * module/oop/goops/compile.scm (next-method?, compile-make-procedure): Remove these, as `method' does it all now, hygienically. --- module/oop/goops.scm | 134 ++++++++++++++++++++++++++--------- module/oop/goops/compile.scm | 32 +-------- 2 files changed, 101 insertions(+), 65 deletions(-) diff --git a/module/oop/goops.scm b/module/oop/goops.scm index d7220d470..8c980485f 100644 --- a/module/oop/goops.scm +++ b/module/oop/goops.scm @@ -477,41 +477,105 @@ (toplevel-define! 'name (make #:name 'name))) (add-method! name (method args body ...)))))) -(define-macro (method args . body) - (letrec ((specializers - (lambda (ls) - (cond ((null? ls) (list (list 'quote '()))) - ((pair? ls) (cons (if (pair? (car ls)) - (cadar ls) - ') - (specializers (cdr ls)))) - (else '())))) - (formals - (lambda (ls) - (if (pair? ls) - (cons (if (pair? (car ls)) (caar ls) (car ls)) - (formals (cdr ls))) - ls)))) - (let ((make-proc (compile-make-procedure (formals args) - (specializers args) - body))) - `(make - #:specializers (cons* ,@(specializers args)) - #:formals ',(formals args) - #:body ',body - #:make-procedure ,make-proc - #:procedure ,(and (not make-proc) - ;; that is to say: we set #:procedure if - ;; `compile-make-procedure' returned `#f', - ;; which is the case if `body' does not - ;; contain a call to `next-method' - `(lambda ,(formals args) - ,@(if (null? body) - ;; This used to be '((begin)), but - ;; guile's memoizer doesn't like - ;; (lambda args (begin)). - '((if #f #f)) - body))))))) +(define-syntax method + (lambda (x) + (define (compute-formals args) + (let lp ((ls args) (out '())) + (syntax-case ls () + (((f s) . rest) (lp (syntax rest) (cons (syntax f) out))) + ((f . rest) (identifier? (syntax f)) + (lp (syntax rest) (cons (syntax f) out))) + (() (reverse out)) + (tail (identifier? (syntax tail)) + (append (reverse out) (syntax tail)))))) + + (define (compute-specializers args) + (let lp ((ls args) (out '())) + (syntax-case ls () + (((f s) . rest) (lp (syntax rest) (cons (syntax s) out))) + ((f . rest) (lp (syntax rest) (cons (syntax ) out))) + (() (reverse (cons (syntax '()) out))) + (tail (reverse (cons (syntax ) out)))))) + + (define (find-free-id exp referent) + (syntax-case exp () + ((x . y) + (or (find-free-id (syntax x) referent) + (find-free-id (syntax y) referent))) + (x + (identifier? (syntax x)) + (let ((id (datum->syntax (syntax x) referent))) + (and (free-identifier=? (syntax x) id) id))) + (_ #f))) + + (define (compute-procedure formals body) + (syntax-case body () + ((body0 ...) + (with-syntax ((formals formals)) + (syntax (lambda formals body0 ...)))))) + + (define (->proper args) + (let lp ((ls args) (out '())) + (syntax-case ls () + ((x . xs) (lp (syntax xs) (cons (syntax x) out))) + (() (reverse out)) + (tail (reverse (cons (syntax tail) out)))))) + + (define (compute-make-procedure formals body next-method) + (syntax-case body () + ((body ...) + (with-syntax ((next-method next-method)) + (syntax-case formals () + ((formal ...) + (syntax + (lambda (real-next-method) + (lambda (formal ...) + (let ((next-method (lambda args + (if (null? args) + (real-next-method formal ...) + (apply real-next-method args))))) + body ...))))) + (formals + (with-syntax (((formal ...) (->proper (syntax formals)))) + (syntax + (lambda (real-next-method) + (lambda formals + (let ((next-method (lambda args + (if (null? args) + (apply real-next-method formal ...) + (apply real-next-method args))))) + body ...))))))))))) + + (define (compute-procedures formals body) + ;; So, our use of this is broken, because it operates on the + ;; pre-expansion source code. It's equivalent to just searching + ;; for referent in the datums. Ah well. + (let ((id (find-free-id body 'next-method))) + (if id + ;; return a make-procedure + (values (syntax #f) + (compute-make-procedure formals body id)) + (values (compute-procedure formals body) + (syntax #f))))) + + (syntax-case x () + ((_ args) (syntax (method args (if #f #f)))) + ((_ args body0 body1 ...) + (with-syntax ((formals (compute-formals (syntax args))) + ((specializer ...) (compute-specializers (syntax args)))) + (call-with-values + (lambda () + (compute-procedures (syntax formals) (syntax (body0 body1 ...)))) + (lambda (procedure make-procedure) + (with-syntax ((procedure procedure) + (make-procedure make-procedure)) + (syntax + (make + #:specializers (cons* specializer ...) + #:formals 'formals + #:body '(body0 body1 ...) + #:make-procedure make-procedure + #:procedure procedure)))))))))) ;;; ;;; {add-method!} diff --git a/module/oop/goops/compile.scm b/module/oop/goops/compile.scm index 3962be4bc..e6b13c416 100644 --- a/module/oop/goops/compile.scm +++ b/module/oop/goops/compile.scm @@ -24,7 +24,7 @@ (define-module (oop goops compile) :use-module (oop goops) :use-module (oop goops util) - :export (compute-cmethod compile-make-procedure) + :export (compute-cmethod) :no-backtrace ) @@ -60,9 +60,7 @@ ;;; So, for the reader: there basic idea is that, given that the ;;; semantics of `next-method' depend on the concrete types being ;;; dispatched, why not compile a specific procedure to handle each type -;;; combination that we see at runtime. There are two compilation -;;; strategies implemented: one for the memoizer, and one for the VM -;;; compiler. +;;; combination that we see at runtime. ;;; ;;; In theory we can do much better than a bytecode compilation, because ;;; we know the *exact* types of the arguments. It's ideal for native @@ -71,32 +69,6 @@ ;;; I think this whole generic application mess would benefit from a ;;; strict MOP. -;;; Temporary solution---return #f if x doesn't refer to `next-method'. -(define (next-method? x) - (and (pair? x) - (or (eq? (car x) 'next-method) - (next-method? (car x)) - (next-method? (cdr x))))) - -;; Called by the `method' macro in goops.scm. -(define (compile-make-procedure formals specializers body) - (and (next-method? body) - (let ((next-method-sym (gensym " next-method")) - (args-sym (gensym))) - `(lambda (,next-method-sym) - (lambda ,formals - (let ((next-method (lambda ,args-sym - (if (null? ,args-sym) - ,(if (list? formals) - `(,next-method-sym ,@formals) - `(apply - ,next-method-sym - ,@(improper->proper formals))) - (apply ,next-method-sym ,args-sym))))) - ,@(if (null? body) - '((begin)) - body))))))) - (define (compile-method methods types) (let ((make-procedure (slot-ref (car methods) 'make-procedure))) (if make-procedure -- 2.20.1