~ chicken-core (chicken-5) 40a2249b0b948682b90533eb66bea0519f251a5f
commit 40a2249b0b948682b90533eb66bea0519f251a5f Author: felix <felix@call-with-current-continuation.org> AuthorDate: Sat Feb 5 16:27:16 2011 +0100 Commit: felix <felix@call-with-current-continuation.org> CommitDate: Sat Feb 5 16:27:16 2011 +0100 added patch by sjamaan for tail-pattern support in syntax-rules diff --git a/expand.scm b/expand.scm index 5d4f3931..f8f44318 100644 --- a/expand.scm +++ b/expand.scm @@ -1995,3 +1995,27 @@ (set-module-sexports! mod sexports)))))) (define ##sys#module-table '()) + + +;; Used by the syntax-rules implementation (and possibly handy elsewhere) +;; (kindly contributed by Peter Bex) + +(define (##sys#drop-right input temp) + ;;XXX use unsafe accessors + (let loop ((len (length input)) + (input input)) + (cond + ((> len temp) + (cons (car input) + (loop (- len 1) (cdr input)))) + (else '())))) + +(define (##sys#take-right input temp) + ;;XXX use unsafe accessors + (let loop ((len (length input)) + (input input)) + (cond + ((> len temp) + (loop (- len 1) (cdr input))) + (else input)))) + diff --git a/manual/Macros b/manual/Macros index 858e1dbe..d4ee169c 100644 --- a/manual/Macros +++ b/manual/Macros @@ -42,9 +42,15 @@ is expanded into (letrec-syntax ((foo ...) (bar ...)) ...) ) -{{syntax-rules}} partially supports [[http://srfi.schemers.org/srfi-46/|SRFI-46]] +{{syntax-rules}} supports [[http://srfi.schemers.org/srfi-46/|SRFI-46]] in allowing the ellipsis identifier to be user-defined by passing it as the first -argument to the {{syntax-rules}} form. +argument to the {{syntax-rules}} form. Also, "tail" patterns of the form + + (syntax-rules () + ((_ (a b ... c) + ... + +are supported. The effect of destructively modifying the s-expression passed to a transformer procedure is undefined. diff --git a/synrules.scm b/synrules.scm index 0e5f66fe..32f09fbb 100644 --- a/synrules.scm +++ b/synrules.scm @@ -65,14 +65,17 @@ (define %and (r 'and)) (define %car '##sys#car) (define %cdr '##sys#cdr) + (define %length (r 'length)) (define %vector? '##sys#vector?) (define %vector-length '##sys#vector-length) (define %vector-ref '##sys#vector-ref) (define %vector->list '##sys#vector->list) (define %list->vector '##sys#list->vector) (define %>= '##sys#>=) + (define %> (r '>)) (define %= '##sys#=) (define %+ '##sys#+) + (define %- '-) (define %i (r 'i)) (define %compare (r 'compare)) (define %cond (r 'cond)) @@ -82,6 +85,7 @@ (define %equal? '##sys#equal?) (define %input (r 'input)) (define %l (r 'l)) + (define %len (r 'len)) (define %lambda (r 'lambda)) (define %let (r 'let)) (define %let* (r 'let*)) @@ -116,107 +120,83 @@ (null? (cddr rule))) (let ((pattern (cdar rule)) (template (cadr rule))) - `((,%and ,@(process-match %tail pattern)) + `((,%and ,@(process-match %tail pattern #f)) (,%let* ,(process-pattern pattern %tail - (lambda (x) x)) + (lambda (x) x) #f) ,(process-template template 0 - (meta-variables pattern 0 '()))))) + (meta-variables pattern 0 '() #f))))) (##sys#syntax-error-hook "ill-formed syntax rule" rule))) ;; Generate code to test whether input expression matches pattern - (define (process-match input pattern) + (define (process-match input pattern seen-segment?) (cond ((symbol? pattern) (if (memq pattern subkeywords) `((,%compare ,input (,%rename (##core#syntax ,pattern)))) `())) - ((segment-pattern? pattern) - (process-segment-match input (car pattern))) + ((segment-pattern? pattern seen-segment?) + (process-segment-match input pattern)) ((pair? pattern) `((,%let ((,%temp ,input)) - (,%and (,%pair? ,%temp) - ,@(process-match `(,%car ,%temp) (car pattern)) - ,@(process-match `(,%cdr ,%temp) (cdr pattern)))))) + (,%and (,%pair? ,%temp) + ,@(process-match `(,%car ,%temp) (car pattern) #f) + ,@(process-match `(,%cdr ,%temp) (cdr pattern) #f))))) ((vector? pattern) - (process-vector-match input pattern)) + `((,%let ((,%temp ,input)) + (,%and (,%vector? ,%temp) + ,@(process-match `(,%vector->list ,%temp) + (vector->list pattern) #f))))) ((or (null? pattern) (boolean? pattern) (char? pattern)) `((,%eq? ,input ',pattern))) (else `((,%equal? ,input ',pattern))))) (define (process-segment-match input pattern) - (let ((conjuncts (process-match `(,%car ,%l) pattern))) - (if (null? conjuncts) - `((,%list? ,input)) ;+++ - `((,%let ,%loop ((,%l ,input)) - (,%or (,%null? ,%l) - (,%and (,%pair? ,%l) - ,@conjuncts - (,%loop (,%cdr ,%l))))))))) - - (define (process-vector-match input pattern) - (let* ((len (vector-length pattern)) - (segment? (and (>= len 2) - (ellipsis? (vector-ref pattern (- len 1)))))) - `((,%let ((,%temp ,input)) - (,%and (,%vector? ,%temp) - ,(if segment? - `(,%>= (,%vector-length ,%temp) ,(- len 2)) - `(,%= (,%vector-length ,%temp) ,len)) - ,@(let lp ((i 0)) - (cond - ((>= i len) - '()) - ((and (= i (- len 2)) segment?) - `((,%let ,%loop ((,%i ,i)) - (,%or (,%>= ,%i ,len) - (,%and ,@(process-match - `(,%vector-ref ,%temp ,%i) - (vector-ref pattern (- len 2))) - (,%loop (,%+ ,%i 1))))))) - (else - (append (process-match `(,%vector-ref ,%temp ,i) - (vector-ref pattern i)) - (lp (+ i 1))))))))))) - + (let ((conjuncts (process-match `(,%car ,%l) (car pattern) #f))) + `((,%and (,%list? ,input) ; Can't ask for its length if not a proper list + (,%let ((,%len (,%length ,input))) + (,%and (,%>= ,%len ,(length (cddr pattern))) + (,%let ,%loop ((,%l ,input) + (,%len ,%len)) + (,%cond + ((,%= ,%len ,(length (cddr pattern))) + ,@(process-match %l (cddr pattern) #t)) + (,%else + (,%and ,@conjuncts + (,%loop (,%cdr ,%l) (,%- ,%len 1)))))))))))) + ;; Generate code to take apart the input expression ;; This is pretty bad, but it seems to work (can't say why). - (define (process-pattern pattern path mapit) + (define (process-pattern pattern path mapit seen-segment?) (cond ((symbol? pattern) (if (memq pattern subkeywords) '() (list (list pattern (mapit path))))) - ((segment-pattern? pattern) - (process-pattern (car pattern) - %temp - (lambda (x) ;temp is free in x - (mapit (if (eq? %temp x) - path ;+++ - `(,%map1 (,%lambda (,%temp) ,x) - ,path)))))) + ((segment-pattern? pattern seen-segment?) + (let* ((tail-length (length (cddr pattern))) + (%match (if (zero? tail-length) ; Simple segment? + path ; No list traversing overhead at runtime! + `(##sys#drop-right ,path ,tail-length)))) + (append + (process-pattern (car pattern) + %temp + (lambda (x) ;temp is free in x + (mapit + (if (eq? %temp x) + %match ; Optimization: no map+lambda + `(,%map1 (,%lambda (,%temp) ,x) ,%match)))) + #f) + (process-pattern (cddr pattern) + `(##sys#take-right ,path ,tail-length) mapit #t)))) ((pair? pattern) - (append (process-pattern (car pattern) `(,%car ,path) mapit) - (process-pattern (cdr pattern) `(,%cdr ,path) mapit))) + (append (process-pattern (car pattern) `(,%car ,path) mapit #f) + (process-pattern (cdr pattern) `(,%cdr ,path) mapit #f))) ((vector? pattern) - (let* ((len (vector-length pattern)) - (segment? (and (>= len 2) - (ellipsis? (vector-ref pattern (- len 1)))))) - (if segment? - (process-pattern (vector->list pattern) - `(,%vector->list ,path) - mapit) - (let lp ((i 0)) - (cond - ((>= i len) - '()) - (else - (append (process-pattern (vector-ref pattern i) - `(,%vector-ref ,path ,i) - mapit) - (lp (+ i 1))))))))) + (process-pattern (vector->list pattern) + `(,%vector->list ,path) mapit #f)) (else '()))) ;; Generate code to compose the output expression according to template @@ -266,18 +246,19 @@ ;; Return an association list of (var . dim) - (define (meta-variables pattern dim vars) + (define (meta-variables pattern dim vars seen-segment?) (cond ((symbol? pattern) (if (memq pattern subkeywords) vars (cons (cons pattern dim) vars))) - ((segment-pattern? pattern) - (meta-variables (car pattern) (+ dim 1) vars)) + ((segment-pattern? pattern seen-segment?) + (meta-variables (car pattern) (+ dim 1) + (meta-variables (cddr pattern) dim vars #t) #f)) ((pair? pattern) (meta-variables (car pattern) dim - (meta-variables (cdr pattern) dim vars))) + (meta-variables (cdr pattern) dim vars #f) #f)) ((vector? pattern) - (meta-variables (vector->list pattern) dim vars)) + (meta-variables (vector->list pattern) dim vars #f)) (else vars))) ;; Return a list of meta-variables of given higher dim @@ -303,10 +284,14 @@ (free-meta-variables (vector->list template) dim env free)) (else free))) - (define (segment-pattern? pattern) - (and (segment-template? pattern) - (or (null? (cddr pattern)) - (##sys#syntax-error-hook "segment matching not implemented" pattern)))) + (define (segment-pattern? p seen-segment?) + (and (segment-template? p) + (cond + (seen-segment? + (##sys#syntax-error-hook "Only one segment per level is allowed" p)) + ((not (list? p)) ; Improper list + (##sys#syntax-error-hook "Cannot combine dotted tail and ellipsis" p)) + (else #t)))) (define (segment-template? pattern) (and (pair? pattern) diff --git a/tests/syntax-tests.scm b/tests/syntax-tests.scm index 9200aafb..45a4d01a 100644 --- a/tests/syntax-tests.scm +++ b/tests/syntax-tests.scm @@ -196,7 +196,7 @@ (bar foo)))) ) -;;; alternative ellipsis test +;;; alternative ellipsis test (SRFI-46) (define-syntax foo (syntax-rules @@ -218,6 +218,56 @@ (t 3 (inc 2)) +;;; Rest patterns after ellipsis (SRFI-46) + +(define-syntax foo + (syntax-rules () + ((_ (a ... b) ... (c d)) + (list (list (list a ...) ... b ...) c d)) + ((_ #(a ... b) ... #(c d) #(e f)) + (list (list (vector a ...) ... b ...) c d e f)) + ((_ #(a ... b) ... #(c d)) + (list (list (vector a ...) ... b ...) c d)))) + +(t '(() 1 2) + (foo (1 2))) + +(t '(((1) 2) 3 4) + (foo (1 2) (3 4))) + +(t '(((1 2) (4) 3 5) 6 7) + (foo (1 2 3) (4 5) (6 7))) + +(t '(() 1 2) + (foo #(1 2))) + +(t '((#() 1) 2 3) + (foo #(1) #(2 3))) + +(t '((#(1 2) 3) 4 5) + (foo #(1 2 3) #(4 5))) + +(t '((#(1 2) 3) 4 5 6 7) + (foo #(1 2 3) #(4 5) #(6 7))) + +(t '(() 1 2 3 4) + (foo #(1 2) #(3 4))) + +(t '((#(1) 2) 3 4 5 6) + (foo #(1 2) #(3 4) #(5 6))) + +(t '((#(1 2) #(4) 3 5) 6 7 8 9) + (foo #(1 2 3) #(4 5) #(6 7) #(8 9))) + +;;; Bug discovered during implementation of SRFI-46 rest patterns: + +(define-syntax foo + (syntax-rules () + ((_ #((a) ...)) (list a ...)))) + +(t '(1) + (foo #((1)))) + ;;; (define-syntax usetmpTrap