New, improved let-opt. More features.
But I have a whole different design for handling optional args, so this will all go away at some point.
This commit is contained in:
		
							parent
							
								
									d17b4b675e
								
							
						
					
					
						commit
						c1b9253b70
					
				
							
								
								
									
										691
									
								
								scsh/let-opt.scm
								
								
								
								
							
							
						
						
									
										691
									
								
								scsh/let-opt.scm
								
								
								
								
							|  | @ -1,45 +1,66 @@ | |||
| ;;; LET-OPTIONALS macros | ||||
| ;;; Copyright (c) 2001 by Olin Shivers. | ||||
| ;;; See file COPYING. | ||||
| 
 | ||||
| ;;; This file defines three macros for parsing optional arguments to procs: | ||||
| ;;; 	(LET-OPTIONALS  arg-list ((var1 default1) ...) . body) | ||||
| ;;; 	(LET-OPTIONALS* arg-list ((var1 default1) ...) . body) | ||||
| ;;; 	(:OPTIONAL rest-arg default-exp) | ||||
| ;;; 	(LET-OPTIONALS  arg-list (opt-clause1 ... opt-clauseN [rest])  | ||||
| ;;;       body ...) | ||||
| ;;; 	(LET-OPTIONALS* arg-list (opt-clause1 ... opt-clauseN [rest]) | ||||
| ;;;       body ...) | ||||
| ;;; 	(:OPTIONAL rest-arg default-exp [arg-check]) | ||||
| ;;; where | ||||
| ;;;     <opt-clause> ::= (var default [arg-check supplied?]) | ||||
| ;;;                  |   ((var1 ... varN) external-arg-parser) | ||||
| ;;; | ||||
| ;;; The LET-OPTIONALS macro is defined using the Clinger/Rees | ||||
| ;;; explicit-renaming low-level macro system. You'll have to do some work to | ||||
| ;;; port it to another macro system. | ||||
| ;;; LET-OPTIONALS* has LET* scope -- each arg clause sees the bindings of | ||||
| ;;; the previous clauses. LET-OPTIONALS has LET scope -- each arg clause | ||||
| ;;; sees the outer scope (an ARG-CHECK expression sees the outer scope | ||||
| ;;; *plus* the variable being bound by that clause, by necessity). | ||||
| ;;; | ||||
| ;;; The LET-OPTIONALS* and :OPTIONAL macros are defined with simple | ||||
| ;;; high-level macros, and should be portable to any R4RS system. | ||||
| ;;; | ||||
| ;;; These macros are all careful to evaluate their default forms *only* if | ||||
| ;;; their values are needed. | ||||
| ;;; | ||||
| ;;; The top-level forms in this file are Scheme 48 module expressions. | ||||
| ;;; I use the module system to help me break up the expander code for  | ||||
| ;;; LET-OPTIONALS into three procedures, which makes it easier to understand | ||||
| ;;; and test. But if you wanted to port this code to a module-less Scheme | ||||
| ;;; system, you'd probably have to inline the three procs into the actual | ||||
| ;;; macro definition. | ||||
| ;;; In practice, LET-OPTIONALS* is the one you want. | ||||
| ;;; | ||||
| ;;; The only interesting module that is exported by this file is | ||||
| ;;; 	LET-OPT | ||||
| ;;; which obeys the following interface: | ||||
| ;;;     (exports (let-optionals  :syntax) | ||||
| ;;;              (let-optionals* :syntax) | ||||
| ;;;		 (:optional       :syntax)) | ||||
| ;;;		 (:optional      :syntax)) | ||||
| ;;; | ||||
| ;;; The LET-OPTIONALS macro is defined using the Clinger/Rees | ||||
| ;;; explicit-renaming low-level macro system. You'll have to do some work to | ||||
| ;;; port it to another macro system. | ||||
| ;;; | ||||
| ;;; The :OPTIONAL macro is defined with simple high-level macros,  | ||||
| ;;; and should be portable to any R4RS system. | ||||
| ;;; | ||||
| ;;; These macros are all careful to evaluate their default forms *only* if | ||||
| ;;; their values are needed. | ||||
| ;;; | ||||
| ;;; The LET-OPTIONALS expander is pretty hairy. Sorry. It does produce | ||||
| ;;; very good code. | ||||
| ;;; | ||||
| ;;; The top-level forms in this file are Scheme 48 module expressions. | ||||
| ;;; I use the module system to help me break up the expander code for  | ||||
| ;;; LET-OPTIONALS into three procedures, which makes it easier to understand | ||||
| ;;; and test. But if you wanted to port this code to a module-less Scheme | ||||
| ;;; system, you'd probably have to inline the auxiliary procs into the actual | ||||
| ;;; macro definition. | ||||
| ;;; | ||||
| ;;; To repeat: This code is not simple Scheme code; it is module code.  | ||||
| ;;; It must be loaded into the Scheme 48 ,config package, not the ,user  | ||||
| ;;; package.  | ||||
| ;;; | ||||
| ;;; The only non-R4RS dependencies in the macros are ERROR  | ||||
| ;;; The only non-R4RS dependencies in the macros are ERROR, RECEIVE, | ||||
| ;;; and CALL-WITH-VALUES. | ||||
| ;;;  | ||||
| ;;; See below for details on each macro. | ||||
| ;;; 	-Olin | ||||
| 
 | ||||
| ;;; (LET-OPTIONALS arg-list ((var1 default1) ...)  | ||||
| ;;;   body | ||||
| ;;;   ...) | ||||
| ;;; (LET-OPTIONALS* arg-list (clause ... [rest]) body ...) | ||||
| ;;; (LET-OPTIONALS  arg-list (clause ... [rest]) body ...) | ||||
| ;;;  | ||||
| ;;; clause ::= (var default [arg-test supplied?])	; The simple case | ||||
| ;;;        |   ((var1 ...) external-arg-parser)		; external hook | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;;; This form is for binding a procedure's optional arguments to either | ||||
| ;;; the passed-in values or a default. | ||||
|  | @ -49,130 +70,385 @@ | |||
| ;;; the remaining VARi are bound to their corresponding DEFAULTi values. | ||||
| ;;; It is an error if there are more args than variables. | ||||
| ;;; | ||||
| ;;; Simple example: | ||||
| ;;;     (let-optionals* args ((in     (current-input-port)) | ||||
| ;;;                           (out    (current-output-port)) | ||||
| ;;;                           (nbytes (string-length s))) | ||||
| ;;;       ...) | ||||
| ;;; | ||||
| ;;; - The default expressions are *not* evaluated unless needed. | ||||
| ;;; | ||||
| ;;; - When evaluated, the default expressions are carried out in the *outer* | ||||
| ;;;   environment. That is, the DEFAULTi forms do *not* see any of the VARi | ||||
| ;;;   bindings. | ||||
| ;;; - When a LET-OPTIONALS* form is evaluated, the default expressions are  | ||||
| ;;;   carried out in a "sequential" LET*-style scope -- each clause is | ||||
| ;;;   evaluated in a scope that sees the bindings introduced by the previous | ||||
| ;;;   clauses. | ||||
| ;;; | ||||
| ;;;   I originally wanted to have the DEFAULTi forms get eval'd in a LET* | ||||
| ;;;   style scope -- DEFAULT3 would see VAR1 and VAR2, etc. But this is | ||||
| ;;;   impossible to implement without side effects or redundant conditional | ||||
| ;;;   tests. If I drop this requirement, I can use the efficient expansion | ||||
| ;;;   shown below. If you need LET* scope, use the less-efficient  | ||||
| ;;;   LET-OPTIONALS* form defined below. | ||||
| ;;; - LET-OPTIONALS, in contrast, evaluates all clauses in the *outer* | ||||
| ;;;   environment. Each ARG-TEST form, however, does see the variable | ||||
| ;;;   bound by that clause (see below). | ||||
| ;;; | ||||
| ;;; - If there's an ARG-TEST form, it is evaluated when an argument is | ||||
| ;;;   passed in; it is not evaluated when the argument is defaulted. | ||||
| ;;;   If it produces false, an error is raised. You can stick an arg-checking | ||||
| ;;;   expression here. Here's the above example with full arg-checking: | ||||
| ;;;     (let ((strlen (string-length s))) | ||||
| ;;;       (let-optionals args ((in  (current-input-port)  (input-port? in)) | ||||
| ;;;                            (out (current-output-port) (output-port? out)) | ||||
| ;;;                            (nbytes strlen (and (integer? nbytes) | ||||
| ;;;                                                (< -1 nbytes strlen)))) | ||||
| ;;;         ...)) | ||||
| ;;; | ||||
| ;;;   The ARG-TEST expression is evaluated in the outer scope of the LET, | ||||
| ;;;   plus a binding for the parameter being checked. | ||||
| ;;; | ||||
| ;;; - A SUPPLIED? variable is bound to true/false depending on whether or | ||||
| ;;;   not a value was passed in by the caller for this parameter. | ||||
| ;;;  | ||||
| ;;; - If there's a final REST variable in the binding list, it is bound | ||||
| ;;;   to any leftover unparsed values from ARG-LIST. If there isn't a final | ||||
| ;;;   REST var, it is an error to have extra values left. You can use this | ||||
| ;;;   feature to parse a couple of arguments with LET-OPTIONALS, and handle | ||||
| ;;;   following args with some other mechanism. It is also useful for | ||||
| ;;;   procedures whose final arguments are homogeneous. | ||||
| ;;; | ||||
| ;;; - A clause of the form ((var1 ... varn) external-arg-parser) allows you | ||||
| ;;;   to parse & arg-check a group of arguments together. EXTERNAL-ARG-PARSER | ||||
| ;;;   is applied to the argument list. It returns n+1 values: one | ||||
| ;;;   for the leftover argument list, and one for each VARi. | ||||
| ;;; | ||||
| ;;;   This facility is intended for things like substring start/end index  | ||||
| ;;;   pairs. You can abstract out the code for parsing the pair of arguments | ||||
| ;;;   in a separate procedure (parse-substring-index-args args string proc) | ||||
| ;;;   and then a function such as READ-STRING! can simply invoke the procedure | ||||
| ;;;   with a | ||||
| ;;;     ((start end) (lambda (args) (parse-substring-index-args args s read-string!))) | ||||
| ;;;   clause. That is, the external-arg parser facility is a hook | ||||
| ;;;   that lets you interface other arg parsers into LET-OPTIONALS. | ||||
| 
 | ||||
| ;;; Expanding the form | ||||
| ;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;;; We expand the form into a code DAG that avoids repeatedly testing the | ||||
| ;;; arg list once it runs out, but still shares code. For example, | ||||
| ;;; | ||||
| ;;; Example: | ||||
| ;;; (define (read-string! str . maybe-args) | ||||
| ;;;   (let-optionals maybe-args ((port (current-input-port)) | ||||
| ;;;                              (start 0) | ||||
| ;;;                              (end (string-length str))) | ||||
| ;;;   (let-optionals* maybe-args ((port (current-input-port)) | ||||
| ;;;                               (start 0) | ||||
| ;;;                               (end (string-length str))) | ||||
| ;;;     ...)) | ||||
| ;;; | ||||
| ;;; expands to: | ||||
| ;;;  | ||||
| ;;; (let* ((body (lambda (port start end) ...)) | ||||
| ;;;        (end-def (lambda (%port %start) (body %port %start <end-default>))) | ||||
| ;;;        (start-def (lambda (%port) (end-def %port <start-default>))) | ||||
| ;;;        (end-def (lambda (port start) (body port start <end-default>))) | ||||
| ;;;        (start-def (lambda (port) (end-def port <start-default>))) | ||||
| ;;;        (port-def  (lambda () (start-def <port-def>)))) | ||||
| ;;;   (if (null? rest) (port-def) | ||||
| ;;;       (let ((%port (car rest)) | ||||
| ;;; 	        (rest (cdr rest))) | ||||
| ;;; 	  (if (null? rest) (start-def %port) | ||||
| ;;; 	      (let ((%start (car rest)) | ||||
| ;;; 		    (rest (cdr rest))) | ||||
| ;;; 	        (if (null? rest) (end-def %port %start) | ||||
| ;;; 		    (let ((%end (car rest)) | ||||
| ;;; 			  (rest (cdr rest))) | ||||
| ;;; 		      (if (null? rest) (body %port %start %end) | ||||
| ;;; 			  (error ...))))))))) | ||||
| ;;;   (if (pair? tail) | ||||
| ;;;       (let ((port (car tail)) | ||||
| ;;;             (tail (cdr tail))) | ||||
| ;;;         (if (pair? tail) | ||||
| ;;;             (let ((start (car tail)) | ||||
| ;;;                   (tail (cdr tail))) | ||||
| ;;;               (if (pair? tail) | ||||
| ;;;                   (let ((end (car tail)) | ||||
| ;;;                         (tail (cdr tail))) | ||||
| ;;;                     (if (pair? tail) | ||||
| ;;;                         (error ...) | ||||
| ;;;                         (body port start end))) | ||||
| ;;;                   (end-def port start))) | ||||
| ;;;             (start-def port))) | ||||
| ;;;       (port-def))) | ||||
| ;;; | ||||
| ;;; Note that the defaulter code (the chain of ...-DEF procs) is just a | ||||
| ;;; linear sequence of machine code into which the IF-tree branches. Once | ||||
| ;;; we jump into the defaulter chain, we never test the arg list again. | ||||
| ;;; A reasonable compiler can turn this into optimal parameter-parsing code. | ||||
| 
 | ||||
| 
 | ||||
| (define-structure let-opt-expanders (export expand-let-optionals) | ||||
|   (open scheme) | ||||
| (define-structure let-opt-expanders (export expand-let-optionals | ||||
| 					    expand-let-optionals*) | ||||
|   (open scheme | ||||
| 	error-package | ||||
| 	receiving) | ||||
|   (begin | ||||
| 
 | ||||
| (define (make-gensym prefix) | ||||
|  (let ((counter 0)) | ||||
|    (lambda () | ||||
|      (set! counter (+ counter 1)) | ||||
|      (string->symbol (string-append prefix (number->string counter)))))) | ||||
| 
 | ||||
| ;;; This guy makes the END-DEF, START-DEF, PORT-DEF definitions above. | ||||
| ;;; If an elt of VARS is a list, we are dealing with a group-parser clause. | ||||
| ;;; In this case, the corresponding element of DEFS is the name of | ||||
| ;;; the parser. | ||||
| ;;; I wish I had a reasonable loop macro. | ||||
| ;;; | ||||
| ;;; DEFAULTER-NAMES also holds the xparser expressions | ||||
| ;;; - STAR? true | ||||
| ;;;   LET* scope semantics -- default I & xparser I are evaluated in | ||||
| ;;;   a scope that sees vars 1 ... I-1. | ||||
| ;;; - STAR? false | ||||
| ;;;   LET scope semantics -- default and xparser forms don't see any of the | ||||
| ;;;   vars. | ||||
| ;;; | ||||
| ;;; I considered documenting this procedure better, but finally decided | ||||
| ;;; that if it was this hard for me to write, it should be hard for you | ||||
| ;;; to read. -Olin | ||||
| 
 | ||||
| (define (make-default-procs vars body-proc defaulter-names defs rename) | ||||
|   (let ((%lambda (rename 'lambda))) | ||||
|     (let recur ((vars (reverse vars)) | ||||
| 		(defaulter-names (reverse defaulter-names)) | ||||
| 		(defs (reverse defs)) | ||||
| 		(next-guy body-proc)) | ||||
|       (if (null? vars) '() | ||||
| 	  (let ((vars (cdr vars))) | ||||
| 	    `((,(car defaulter-names) | ||||
| 	       (,%lambda ,(reverse vars) | ||||
| 			 (,next-guy ,@(reverse vars) ,(car defs)))) | ||||
| 	      . ,(recur vars | ||||
| 			(cdr defaulter-names) | ||||
| 			(cdr defs) | ||||
| 			(car defaulter-names)))))))) | ||||
| (define (make-default-procs vars body-proc defaulter-names defs | ||||
| 			    sup-vars rest-var star? rename) | ||||
|   (receive (defaulters ignore-me and-me-too) | ||||
|       (really-make-default-procs vars body-proc defaulter-names defs | ||||
| 				 sup-vars rest-var star? rename) | ||||
|     (reverse defaulters))) | ||||
| 
 | ||||
| (define (really-make-default-procs vars body-proc defaulter-names defs | ||||
| 				   sup-vars rest-var star? rename) | ||||
|   (let ((%lambda (rename 'lambda)) | ||||
| 	(%let (rename 'let)) | ||||
| 	(%ignore (rename '_)) | ||||
| 	(%call/values (rename 'call-with-values)) | ||||
| 	(tail (rename 'tail)) | ||||
| 	(make-rv (let ((g (make-gensym "%ov."))) | ||||
| 		   (lambda x (rename (g))))) | ||||
| 	(make-sv (let ((g (make-gensym "%sv."))) | ||||
| 		   (lambda () (rename (g)))))) | ||||
| 
 | ||||
|     ;; RECUR returns 2 values: a LET*-binding list of defaulter proc | ||||
|     ;; bindings, and an expression to evaluate in their scope. | ||||
|     (let recur ((vars vars) | ||||
| 		(rev-params '())	; These guys | ||||
| 		(rev-vals '())		; have these values. | ||||
| 		(sup-vars sup-vars)	 | ||||
| 		(rev-sup-params '())	; These guys | ||||
| 		(rev-sup-vals '())	; have these values. | ||||
| 		(defaulter-names defaulter-names) | ||||
| 		(defs defs)) | ||||
|       ;; Note that the #F's bound to the SUPPLIED? parameters have no | ||||
|       ;; effects, and so commute with the evaluation of the defaults. | ||||
|       ;; Hence we don't need the VALS-EVALED? trick for them, just for the | ||||
|       ;; default forms & their parameters. | ||||
|       (if (pair? vars) | ||||
| 	  (let* ((var (car vars)) (vars (cdr vars)) ; "VAR" is really a list | ||||
| 		 (def (car defs)) (defs (cdr defs)) ; in xparser case... | ||||
| 		 (rvar (if star? var	; scope control | ||||
| 			   (if (pair? var) (map make-rv var) (make-rv)))) | ||||
| 		 (rev-params1 (if (pair? rvar) | ||||
| 				  (append (reverse rvar) rev-params) | ||||
| 				  (cons rvar rev-params))) | ||||
| 		 (rev-vals1 (if (pair? rvar) rev-params1 | ||||
| 				(cons def rev-params))) | ||||
| 		 (sv (car sup-vars)) | ||||
| 		 (sv (if (or star? (not sv)) sv (make-sv))) | ||||
| 		 (rev-sup-params1 (if sv (cons sv rev-sup-params) | ||||
| 				      rev-sup-params)) | ||||
| 		 (rev-sup-vals1 (cond (sv (cons #f rev-sup-params)) | ||||
| 				      ((pair? var) rev-sup-vals) | ||||
| 				      (else rev-sup-params))) | ||||
| 		 (defaulter (car defaulter-names)) | ||||
| 		 (defaulter-names (cdr defaulter-names))) | ||||
| 	    (receive (procs exp vals-evaled?) | ||||
| 		     (recur vars rev-params1 rev-vals1 (cdr sup-vars) | ||||
| 			    rev-sup-params1 rev-sup-vals1 | ||||
| 			    defaulter-names defs) | ||||
| 	      (if (pair? var) | ||||
| 		  ;; Return #f for VALS-EVALED? so we'll force any prior | ||||
| 		  ;; default to be eval'd & not pushed below this default eval. | ||||
| 		  (values procs | ||||
| 			  `(,%call/values (,%lambda () (,defaulter '())) | ||||
| 			     (,%lambda ,(cons %ignore rvar) ,exp)) | ||||
| 			  #f) | ||||
| 
 | ||||
| 		  (let ((params (reverse (append rev-sup-params rev-params))) | ||||
| 			(exp (if vals-evaled? exp | ||||
| 				 `(,%let ((,rvar ,def)) ,exp)))) | ||||
| 		    (values `((,defaulter (,%lambda ,params ,exp)) | ||||
| 			      . ,procs) | ||||
| 			    `(,defaulter ,@(reverse rev-vals) | ||||
| 			                 ,@(reverse rev-sup-vals)) | ||||
| 			    #t))))) | ||||
| 
 | ||||
| 	  (values '() `(,body-proc ,@(if rest-var '('()) '()) | ||||
| 				   ,@(reverse rev-vals) | ||||
| 				   . ,(reverse rev-sup-vals)) | ||||
| 		  #t))))) | ||||
| 
 | ||||
| 
 | ||||
| ;;; This guy makes the (IF (NULL? REST) (PORT-DEF) ...) tree above. | ||||
|   | ||||
| (define (make-if-tree vars defaulters body-proc rest rename) | ||||
| ;;; This guy makes the (IF (PAIR? TAIL) ... (PORT-DEF)) tree above. | ||||
| ;;; DEFAULTERS is a list of the names of the defaulter procs & the xparser | ||||
| ;;; forms. | ||||
| 
 | ||||
| (define (make-if-tree vars defaulters arg-tests body-proc | ||||
| 		      tail supvars rest-var star? rename) | ||||
|   (let ((%if (rename 'if)) | ||||
| 	(%null? (rename 'null?)) | ||||
| 	(%pair? (rename 'pair?)) | ||||
| 	(%not (rename 'not)) | ||||
| 	(%error (rename 'error)) | ||||
| 	(%let (rename 'let)) | ||||
| 	(%lambda (rename 'lambda)) | ||||
| 	(%call/values (rename 'call-with-values)) | ||||
| 	(%car (rename 'car)) | ||||
| 	(%cdr (rename 'cdr))) | ||||
| 	(%cdr (rename 'cdr)) | ||||
| 	(make-rv (let ((g (make-gensym "%ov."))) | ||||
| 		   (lambda x (rename (g)))))) | ||||
| 	 | ||||
|     (let recur ((vars vars) (defaulters defaulters) (non-defaults '())) | ||||
|     (let recur ((vars vars) (defaulters defaulters) | ||||
| 		(ats arg-tests) (non-defaults '()) | ||||
| 		(supvars supvars) (sup-trues '())) | ||||
|       (if (null? vars) | ||||
| 	  `(,%if (,%null? ,rest) (,body-proc . ,(reverse non-defaults)) | ||||
| 		 (,%error "Too many optional arguments." ,rest)) | ||||
| 	  (if rest-var | ||||
| 	      `(,body-proc  ,tail ,@(reverse non-defaults) . ,sup-trues) | ||||
| 	      `(,%if (,%pair? ,tail) | ||||
| 		     (,%error "Too many optional arguments." ,tail) | ||||
| 		     (,body-proc ,@(reverse non-defaults) . ,sup-trues))) | ||||
| 
 | ||||
| 	  (let ((v (car vars))) | ||||
| 	    `(,%if (,%null? ,rest) | ||||
| 		   (,(car defaulters) . ,(reverse non-defaults)) | ||||
| 		   (,%let ((,v (,%car ,rest)) | ||||
| 			   (,rest (,%cdr ,rest))) | ||||
| 		     ,(recur (cdr vars) | ||||
| 			     (cdr defaulters) | ||||
| 			     (cons v non-defaults))))))))) | ||||
| 	  (let* ((v (car vars)) | ||||
| 		 (rv (if star? v	; Scope control | ||||
| 			 (if (pair? v) (map make-rv v) (make-rv)))) | ||||
| 		 (at (car ats)) | ||||
| 		 (sup-trues1 (if (car supvars) (cons #t sup-trues) sup-trues)) | ||||
| 
 | ||||
| 		 (body `(,@(if (not (eq? at #t)) | ||||
| 			       (let ((test (if star? at | ||||
| 					       `(,%let ((,v ,rv)) ,at)))) | ||||
| 				 `((,%if (,%not ,test) | ||||
| 					 (,%error "Optional argument failed test" | ||||
| 						  ',at ',v ,rv)))) | ||||
| 			       '())	; No arg test | ||||
| 			 ,(recur (cdr vars) | ||||
| 				 (cdr defaulters) | ||||
| 				 (cdr ats) | ||||
| 				 (if (pair? rv) | ||||
| 				     (append (reverse rv) non-defaults) | ||||
| 				     (cons rv non-defaults)) | ||||
| 				 (cdr supvars) sup-trues1)))) | ||||
| 	    (if (pair? rv) | ||||
| 		`(,%call/values (,%lambda () | ||||
| 			          (,(car defaulters) ,tail)) | ||||
| 		   (,%lambda (,tail . ,rv) . ,body)) | ||||
| 
 | ||||
| 		`(,%if (,%pair? ,tail) | ||||
| 		       (,%let ((,rv (,%car ,tail)) | ||||
| 			       (,tail (,%cdr ,tail))) | ||||
| 		         . ,body) | ||||
| 		       (,(car defaulters) ,@(reverse non-defaults) . ,sup-trues)))))))) | ||||
| 	     | ||||
| 
 | ||||
| (define (expand-let-optionals exp rename compare?) | ||||
| ;;; Parse the clauses into  | ||||
| ;;; - a list of vars,  | ||||
| ;;; - a list of defaults, | ||||
| ;;; - a list of possible arg-tests. No arg-test is represented as #T. | ||||
| ;;; - a list of possible SUPPLIED? vars. An elt is either (var) or #f. | ||||
| ;;; - either the rest var or #f | ||||
| ;;; | ||||
| ;;; This is written out in painful detail so that we can do a lot of | ||||
| ;;; syntax checking. | ||||
| 
 | ||||
| (define (parse-clauses bindings) | ||||
|   ;; LIST-LIB defines EVERY... but uses LET-OPTIONALS. | ||||
|   ;; Define here to break the dependency loop: | ||||
|   (define (every pred lis) | ||||
|     (or (not (pair? lis)) (and (pred (car lis)) (every pred (car lis))))) | ||||
| 
 | ||||
|   (cond ((pair? bindings) | ||||
| 	 (let ((rev (reverse bindings))) | ||||
| 	   (receive (rest-var rev) (if (symbol? (car rev)) | ||||
| 				       (values (car rev) (cdr rev)) | ||||
| 				       (values #f rev)) | ||||
| 	     (receive (vars defs ats supvars) | ||||
| 		 (let recur ((bindings (reverse rev))) | ||||
| 		   (if (not (pair? bindings)) | ||||
| 		       (values '() '() '() '()) | ||||
| 		       (receive (vars defs ats supvars) (recur (cdr bindings)) | ||||
| 			 (let ((binding  (car bindings))) | ||||
| 			   (if (not (and (list? binding) (<= 2 (length binding) 4))) | ||||
| 			       (error "Illegal binding form in LET-OPTIONAL or LET-OPTIONAL*" | ||||
| 				      binding)) | ||||
| 			  | ||||
| 			   (let* ((var (car binding)) | ||||
| 				  (vars (cons var vars)) | ||||
| 				  (defs (cons (cadr binding) defs)) | ||||
| 				  (stuff (cddr binding))) | ||||
| 			     (if (not (or (symbol? var) | ||||
| 					  (and (list? var) | ||||
| 					       (= 2 (length binding)) | ||||
| 					       (every symbol? var)))) | ||||
| 				 (error "Illegal parameter in LET-OPTIONAL or LET-OPTIONAL* binding" | ||||
| 					binding)) | ||||
| 			     (receive (at sup-var) | ||||
| 				 (if (not (pair? stuff)) (values #t #f) | ||||
| 				     (let ((at (car stuff)) | ||||
| 					   (stuff (cdr stuff))) | ||||
| 				       (if (not (pair? stuff)) | ||||
| 					   (values at #f) | ||||
| 					   (let ((sv (car stuff))) | ||||
| 					     (if (not (symbol? sv)) | ||||
| 						 (error "Illegal SUPPLIED? parameter in LET-OPTIONAL or LET-OPTIONAL*" | ||||
| 							binding sv)) | ||||
| 					     (values at sv))))) | ||||
| 			       (values vars defs (cons at ats) (cons sup-var supvars))))))))				        | ||||
| 	       (values vars defs ats supvars rest-var))))) | ||||
| 
 | ||||
| 	((null? bindings) (values '() '() '() '() #f)) | ||||
| 	(else (error "Illegal bindings to LET-OPTIONAL or LET-OPTIONAL* form" | ||||
| 		     bindings)))) | ||||
| 
 | ||||
| (define (really-expand-let-optionals exp star? rename compare?) | ||||
|   (let* ((arg-list (cadr exp)) | ||||
| 	 (var/defs (caddr exp)) | ||||
| 	 (body (cdddr exp)) | ||||
| 	 (vars (map car var/defs)) | ||||
| 
 | ||||
| 	 (prefix-sym (lambda (prefix sym) | ||||
| 		       (string->symbol (string-append prefix (symbol->string sym))))) | ||||
| 
 | ||||
| 	 ;; Private vars, one for each user var. | ||||
| 	 ;; We prefix the % to help keep macro-expanded code from being | ||||
| 	 ;; too confusing. | ||||
| 	 (vars2 (map (lambda (v) (rename (prefix-sym "%" v))) | ||||
| 		     vars)) | ||||
| 
 | ||||
| 	 (defs (map cadr var/defs)) | ||||
| 	 (body-proc (rename 'body)) | ||||
| 
 | ||||
| 	 ;; A private var, bound to the value of the ARG-LIST expression. | ||||
| 	 (rest-var (rename '%rest)) | ||||
| 	 (body-proc  (rename 'body)) | ||||
| 	 (tail-var (rename '%tail))	; Bound to remaining args to be parsed. | ||||
| 
 | ||||
| 	 (%let* (rename 'let*)) | ||||
| 	 (%lambda (rename 'lambda)) | ||||
| 
 | ||||
| 	 (defaulter-names (map (lambda (var) (rename (prefix-sym "def-" var))) | ||||
| 			       vars)) | ||||
| 	 (prefix-sym (lambda (prefix sym) | ||||
| 		       (string->symbol (string-append prefix (symbol->string sym)))))) | ||||
| 
 | ||||
| 	 (defaulters (make-default-procs vars2 body-proc | ||||
| 					 defaulter-names defs rename)) | ||||
| 	 (if-tree (make-if-tree vars2 defaulter-names body-proc | ||||
| 				rest-var rename))) | ||||
|     (receive (vars defs arg-tests maybe-supvars maybe-rest) | ||||
| 	     (parse-clauses var/defs) | ||||
|       (let* ((defaulter-names (map (lambda (var def) | ||||
| 				     (if (pair? var) | ||||
| 					 def	; xparser | ||||
| 					 (rename (prefix-sym "def-" var)))) | ||||
| 				   vars defs)) | ||||
| 	     (rsupvars (if star? maybe-supvars | ||||
| 			   (let ((g (make-gensym "%sv."))) | ||||
| 			     (map (lambda (x) (and x (rename (g)))) | ||||
| 				  maybe-supvars)))) | ||||
| 	     (just-supvars (let recur ((svs maybe-supvars))	; filter | ||||
| 			     (if (not (pair? svs)) '() | ||||
| 				 (let ((sv (car svs)) | ||||
| 				       (tail (recur (cdr svs)))) | ||||
| 				   (if sv (cons sv tail) tail))))) | ||||
| 
 | ||||
| 	     (defaulters (make-default-procs vars body-proc defaulter-names | ||||
| 					     defs rsupvars maybe-rest | ||||
| 					     star? rename)) | ||||
| 
 | ||||
| 	     (if-tree (make-if-tree vars defaulter-names arg-tests body-proc | ||||
| 				    tail-var rsupvars maybe-rest star? rename)) | ||||
| 
 | ||||
| 	     ;; Flatten out the multi-arg items. | ||||
| 	     (allvars (apply append (map (lambda (v) (if (pair? v) v | ||||
| 							 (list v))) | ||||
| 					 vars)))) | ||||
| 
 | ||||
| 	`(,%let* ((,tail-var ,arg-list) | ||||
| 		  (,body-proc (,%lambda ,(append (if maybe-rest | ||||
| 						     (cons maybe-rest allvars) | ||||
| 						     allvars) | ||||
| 						 just-supvars) | ||||
| 			        . ,body)) | ||||
| 		  . ,defaulters) | ||||
| 		 ,if-tree))))) | ||||
| 
 | ||||
| (define (expand-let-optionals exp rename compare?) | ||||
|   (really-expand-let-optionals exp #f rename compare?)) | ||||
| (define (expand-let-optionals* exp rename compare?) | ||||
|   (really-expand-let-optionals exp #t rename compare?)) | ||||
| 
 | ||||
|     `(,%let* ((,rest-var ,arg-list) | ||||
| 	      (,body-proc (,%lambda ,vars . ,body)) | ||||
| 	      . ,defaulters) | ||||
|        ,if-tree))) | ||||
| 
 | ||||
| )) ; erutcurts-enifed | ||||
| ;;; nilO- .noitnevnoc gnitekcarb sugob a ni deppart m'I !pleh !pleh | ||||
|  | @ -188,13 +464,13 @@ | |||
|   (begin | ||||
| 
 | ||||
| 
 | ||||
| ;;; (LET-OPTIONALS args ((var1 default1) ...) body1 ...) | ||||
| ;;; The expander is defined in the code above. | ||||
| ;;; (LET-OPTIONALS  args ((var1 default1 [arg-test supplied?]) ...) body1 ...) | ||||
| ;;; (LET-OPTIONALS* args ((var1 default1 [arg-test supplied?]) ...) body1 ...) | ||||
| 
 | ||||
| (define-syntax let-optionals expand-let-optionals) | ||||
| (define-syntax let-optionals  expand-let-optionals) | ||||
| (define-syntax let-optionals* expand-let-optionals*) | ||||
| 
 | ||||
| 
 | ||||
| ;;; (:optional rest-arg default-exp) | ||||
| ;;; (:optional rest-arg default-exp [test-pred]) | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;;; This form is for evaluating optional arguments and their defaults | ||||
| ;;; in simple procedures that take a *single* optional argument. It is | ||||
|  | @ -205,61 +481,168 @@ | |||
| ;;; - If REST-ARG has 0 elements, evaluate DEFAULT-EXP and return that. | ||||
| ;;; - If REST-ARG has 1 element, return that element. | ||||
| ;;; - If REST-ARG has >1 element, error. | ||||
| ;;; | ||||
| ;;; If there is an TEST-PRED form, it is a predicate that is used to test | ||||
| ;;; a non-default value. If the predicate returns false, an error is raised. | ||||
| 
 | ||||
| (define-syntax :optional | ||||
|   (syntax-rules () | ||||
|     ((:optional rest default-exp) | ||||
|      (let ((maybe-arg rest)) | ||||
|        (cond ((null? maybe-arg) default-exp) | ||||
| 	     ((null? (cdr maybe-arg)) (car maybe-arg)) | ||||
| 	     (else (error "too many optional arguments" maybe-arg))))))) | ||||
|        (if (pair? maybe-arg) | ||||
| 	   (if (null? (cdr maybe-arg)) (car maybe-arg) | ||||
| 	       (error "too many optional arguments" maybe-arg)) | ||||
| 	   default-exp))) | ||||
| 
 | ||||
|     ((:optional rest default-exp arg-test) | ||||
|      (let ((maybe-arg rest)) | ||||
|        (if (pair? maybe-arg) | ||||
| 	   (if (null? (cdr maybe-arg)) | ||||
| 	       (let ((val (car maybe-arg))) | ||||
| 		 (if (arg-test val) val | ||||
| 		     (error "Optional argument failed test" | ||||
| 			    'arg-test val))) | ||||
| 	       (error "too many optional arguments" maybe-arg)) | ||||
| 	   default-exp))))) | ||||
| 
 | ||||
| ;;; (LET-OPTIONALS* args ((var1 default1) ... [rest]) body1 ...) | ||||
| )) ; erutcurts-enifed | ||||
| 
 | ||||
|  | ||||
| ;;; Here is a simpler but less-efficient version of LET-OPTIONALS*. | ||||
| ;;; It redundantly performs end-of-list checks for every optional var, | ||||
| ;;; even after the list runs out. | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| ;;; This is just like LET-OPTIONALS, except that the DEFAULTi forms | ||||
| ;;; are evaluated in a LET*-style environment. That is, DEFAULT3 is evaluated | ||||
| ;;; within the scope of VAR1 and VAR2, and so forth. | ||||
| ;;; | ||||
| ;;; - If the last form in the ((var1 default1) ...) list is not a  | ||||
| ;;;   (VARi DEFAULTi) pair, but a simple variable REST, then it is | ||||
| ;;;   bound to any left-over values. For example, if we have VAR1 through | ||||
| ;;;   VAR7, and ARGS has 9 values, then REST will be bound to the list of | ||||
| ;;;   the two values of ARGS. If ARGS is too short, causing defaults to | ||||
| ;;;   be used, then REST is bound to '(). | ||||
| ;;; - If there is no REST variable, then it is an error to have excess | ||||
| ;;;   values in the ARGS list. | ||||
| 
 | ||||
| 
 | ||||
| ;;; This just interfaces to REALLY-LET-OPTIONALS*, which expects | ||||
| ;;; the ARGS form to be a variable. | ||||
| (define-structure slow-simple-let-opt (export (let-optionals* :syntax)) | ||||
|   (open scheme) | ||||
|   (begin | ||||
| 
 | ||||
| (define-syntax let-optionals* | ||||
|   (syntax-rules () | ||||
|     ((let-optionals* args vars&defaults body1 ...) | ||||
|      (let ((rest args)) | ||||
|        (really-let-optionals* rest vars&defaults body1 ...))))) | ||||
|     ((let-optionals* arg (opt-clause ...) body ...) | ||||
|      (let ((rest arg)) | ||||
|        (let-optionals* rest (opt-clause ...) body ...))))) | ||||
| 
 | ||||
| (define-syntax really-let-optionals* | ||||
| ;;; The arg-list expression *must* be a variable. | ||||
| ;;; (Or must be side-effect-free, in any event.) | ||||
| 
 | ||||
| (define-syntax %let-optionals* | ||||
|   (syntax-rules () | ||||
|     ;; Standard case. Do the first var/default and recurse. | ||||
|     ((really-let-optionals* args ((var1 default1) etc ...) | ||||
|        body1 ...) | ||||
|      (call-with-values (lambda () (if (null? args) | ||||
| 				      (values default1 '()) | ||||
| 				      (values (car args) (cdr args)))) | ||||
| 		       (lambda (var1 rest) | ||||
| 			 (really-let-optionals* rest (etc ...) | ||||
| 			   body1 ...)))) | ||||
|     ((%let-optionals* arg (((var ...) xparser) opt-clause ...) body ...) | ||||
|      (call-with-values (lambda () (xparser arg)) | ||||
|        (lambda (rest var ...) | ||||
|          (%let-optionals* rest (opt-clause ...) body ...)))) | ||||
|      | ||||
|     ((%let-optionals* arg ((var default) opt-clause ...) body ...) | ||||
|      (call-with-values (lambda () (if (null? arg) (values default '()) | ||||
| 				      (values (car arg) (cdr arg)))) | ||||
|        (lambda (var rest) | ||||
| 	 (%let-optionals* rest (opt-clause ...) body ...)))) | ||||
| 
 | ||||
|     ;; Single rest arg -- bind to the remaining rest values. | ||||
|     ((really-let-optionals* args (rest) body1 ...) | ||||
|      (let ((rest args)) body1 ...)) | ||||
|     ((%let-optionals* arg ((var default test) opt-clause ...) body ...) | ||||
|      (call-with-values (lambda () | ||||
| 			 (if (null? arg) (values default '()) | ||||
| 			     (let ((var (car arg))) | ||||
| 			       (if test (values var (cdr arg)) | ||||
| 				   (error "arg failed LET-OPT test" var))))) | ||||
|        (lambda (var rest) | ||||
| 	 (%let-optionals* rest (opt-clause ...) body ...)))) | ||||
| 
 | ||||
|     ;; No more vars. Make sure there are no unaccounted-for values, and | ||||
|     ;; do the body. | ||||
|     ((really-let-optionals* args () body1 ...) | ||||
|      (if (null? args) (begin body1 ...) | ||||
| 	 (error "Too many optional arguments." args))))) | ||||
|     ((%let-optionals* arg ((var default test supplied?) opt-clause ...) body ...) | ||||
|      (call-with-values (lambda () | ||||
| 			 (if (null? arg) (values default #f '()) | ||||
| 			     (let ((var (car arg))) | ||||
| 			       (if test (values var #t (cdr arg)) | ||||
| 				   (error "arg failed LET-OPT test" var))))) | ||||
|        (lambda (var supplied? rest) | ||||
| 	 (%let-optionals* rest (opt-clause ...) body ...)))) | ||||
| 
 | ||||
|     ((%let-optionals* arg (rest) body ...) | ||||
|      (let ((rest arg)) body ...)) | ||||
| 
 | ||||
|     ((%let-optionals* arg () body ...) | ||||
|      (if (null? arg) (begin body ...) | ||||
| 	 (error "Too many arguments in let-opt" arg))))) | ||||
| )) ; erutcurts-enifed | ||||
| 
 | ||||
| 
 | ||||
| ;;; Example derived syntax: | ||||
| ;;; - (fn (var ...) (opt-clause ...) body ...) | ||||
| ;;; - (defn (name var ...) (opt-clause ...) body ...) | ||||
| ;;; - (defn name exp) | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| (define-structure defn-package (export (fn   :syntax) | ||||
| 				       (defn :syntax)) | ||||
|   (open let-opt scheme) | ||||
|   (begin | ||||
| 
 | ||||
| (define-syntax fn | ||||
|   (syntax-rules () | ||||
|     ((fn vars () body ...) (lambda vars body ...)) | ||||
|     ((fn (var ...) opts body ...) | ||||
|      (lambda (var ... . rest) | ||||
|        (let-optionals rest opts body ...))))) | ||||
|       | ||||
| (define-syntax defn | ||||
|   (syntax-rules () | ||||
|     ((defn (name . params) opts body ...) | ||||
|      (define name (fn params opts body ...))) | ||||
|     ((defn name val) (define name val)))) | ||||
| )) ; erutcurts-enifed | ||||
| 
 | ||||
| 
 | ||||
| ;;; Another example derived syntax -- Common-Lisp style fun: | ||||
| ;;;   (FUN (var ... &OPTIONAL opt-clause ... &REST rest-var) body ...) | ||||
| ;;;   (DEFUN (name var ... &OPTIONAL opt-clause ... &REST rest-var) | ||||
| ;;;     body ...) | ||||
| ;;;   (DEFUN name exp)  | ||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||
| 
 | ||||
| (define-structure defun-package (export (fun   :syntax) | ||||
| 					(defun :syntax)) | ||||
|   (open let-opt scheme) | ||||
|   (begin | ||||
| 
 | ||||
| (define-syntax fun | ||||
|   (syntax-rules () | ||||
|     ((fun args body ...) (%fun1 () () () args body ...)))) | ||||
| 
 | ||||
| ;;; This guy basically parses the pieces of the parameter list. | ||||
| (define-syntax %fun1 | ||||
|   (syntax-rules (&optional &rest) | ||||
| 
 | ||||
|     ((%fun1 reg opt () (&optional &rest var) body ...) | ||||
|      (%fun2 reg opt var body ...)) | ||||
| 
 | ||||
|     ((%fun1 reg opt () (&rest var) body ...) | ||||
|      (%fun2 reg opt var body ...)) | ||||
| 
 | ||||
|     ((%fun1 reg opt () (&optional) body ...) | ||||
|      (%fun2 reg opt () body ...)) | ||||
| 
 | ||||
|     ((%fun1 reg opt () () body ...) | ||||
|      (%fun2 reg opt () body ...)) | ||||
| 
 | ||||
|     ((%fun1 reg (opt ...) () (&optional opt1 opt2 ...) body ...) | ||||
|      (%fun1 reg (opt ... opt1) () (&optional opt2 ...) body ...)) | ||||
| 
 | ||||
|     ((%fun1 (var1 ...)      opt () (varn varn+1 ...) body ...) | ||||
|      (%fun1 (var1 ... varn) opt () (varn+1 ...)      body ...)))) | ||||
| 
 | ||||
| ;;; This guy does the expansion into a LET-OPTIONALS*. | ||||
| (define-syntax %fun2 | ||||
|   (syntax-rules () | ||||
|     ((%fun2 (var ...) () rest body ...) | ||||
|      (lambda (var ... . rest) body ...)) | ||||
|     ((%fun2 (v1 ...) opts () body ...) | ||||
|      (lambda (v1 ... . rest) (let-opt rest opts body ...))) | ||||
|     ((%fun2 (v1 ...) (opt1 ...) rest body ...) | ||||
|      (lambda (v1 ... . %rest) (let-opt %rest (opt1 ... rest) body ...))))) | ||||
| 
 | ||||
| (define-syntax defun | ||||
|   (syntax-rules () | ||||
|     ((defun (name arg ...) body ...) | ||||
|      (define name (fun (arg ...) body ...))) | ||||
| 
 | ||||
|     ((defun name exp) (define name exp)))) | ||||
| )) ; erutcurts-enifed | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 olin-shivers
						olin-shivers