style: Allow special forms to be scoped.
* guix/scripts/style.scm (vhashq): Add clause for 'lst, and change default clause. (%special-forms): Add context for 'add-after and 'add-before. Add 'replace. (prefix?, special-form-lead): New procedures. (special-form?): Remove. (pretty-print-with-comments): Add 'context' to the threaded state. Adjust 'print-sequence' and adjust 'loop' calls accordingly. * tests/style.scm: Add tests for 'replace.
This commit is contained in:
		
							parent
							
								
									97d0055edb
								
							
						
					
					
						commit
						208a7aa17b
					
				
					 2 changed files with 73 additions and 27 deletions
				
			
		| 
						 | 
					@ -114,14 +114,19 @@
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define-syntax vhashq
 | 
					(define-syntax vhashq
 | 
				
			||||||
  (syntax-rules ()
 | 
					  (syntax-rules (quote)
 | 
				
			||||||
    ((_) vlist-null)
 | 
					    ((_) vlist-null)
 | 
				
			||||||
 | 
					    ((_ (key (quote (lst ...))) rest ...)
 | 
				
			||||||
 | 
					     (vhash-consq key '(lst ...) (vhashq rest ...)))
 | 
				
			||||||
    ((_ (key value) rest ...)
 | 
					    ((_ (key value) rest ...)
 | 
				
			||||||
     (vhash-consq key value (vhashq rest ...)))))
 | 
					     (vhash-consq key '((() . value)) (vhashq rest ...)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define %special-forms
 | 
					(define %special-forms
 | 
				
			||||||
  ;; Forms that are indented specially.  The number is meant to be understood
 | 
					  ;; Forms that are indented specially.  The number is meant to be understood
 | 
				
			||||||
  ;; like Emacs' 'scheme-indent-function' symbol property.
 | 
					  ;; like Emacs' 'scheme-indent-function' symbol property.  When given an
 | 
				
			||||||
 | 
					  ;; alist instead of a number, the alist gives "context" in which the symbol
 | 
				
			||||||
 | 
					  ;; is a special form; for instance, context (modify-phases) means that the
 | 
				
			||||||
 | 
					  ;; symbol must appear within a (modify-phases ...) expression.
 | 
				
			||||||
  (vhashq
 | 
					  (vhashq
 | 
				
			||||||
   ('begin 1)
 | 
					   ('begin 1)
 | 
				
			||||||
   ('lambda 2)
 | 
					   ('lambda 2)
 | 
				
			||||||
| 
						 | 
					@ -148,9 +153,9 @@
 | 
				
			||||||
   ('operating-system 1)
 | 
					   ('operating-system 1)
 | 
				
			||||||
   ('modify-inputs 2)
 | 
					   ('modify-inputs 2)
 | 
				
			||||||
   ('modify-phases 2)
 | 
					   ('modify-phases 2)
 | 
				
			||||||
   ('add-after 3)
 | 
					   ('add-after '(((modify-phases) . 3)))
 | 
				
			||||||
   ('add-before 3)
 | 
					   ('add-before '(((modify-phases) . 3)))
 | 
				
			||||||
   ;; ('replace 2)
 | 
					   ('replace '(((modify-phases) . 2)))         ;different from 'modify-inputs'
 | 
				
			||||||
   ('substitute* 2)
 | 
					   ('substitute* 2)
 | 
				
			||||||
   ('substitute-keyword-arguments 2)
 | 
					   ('substitute-keyword-arguments 2)
 | 
				
			||||||
   ('call-with-input-file 2)
 | 
					   ('call-with-input-file 2)
 | 
				
			||||||
| 
						 | 
					@ -158,8 +163,30 @@
 | 
				
			||||||
   ('with-output-to-file 2)
 | 
					   ('with-output-to-file 2)
 | 
				
			||||||
   ('with-input-from-file 2)))
 | 
					   ('with-input-from-file 2)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define (special-form? symbol)
 | 
					(define (prefix? candidate lst)
 | 
				
			||||||
  (vhash-assq symbol %special-forms))
 | 
					  "Return true if CANDIDATE is a prefix of LST."
 | 
				
			||||||
 | 
					  (let loop ((candidate candidate)
 | 
				
			||||||
 | 
					             (lst lst))
 | 
				
			||||||
 | 
					    (match candidate
 | 
				
			||||||
 | 
					      (() #t)
 | 
				
			||||||
 | 
					      ((head1 . rest1)
 | 
				
			||||||
 | 
					       (match lst
 | 
				
			||||||
 | 
					         (() #f)
 | 
				
			||||||
 | 
					         ((head2 . rest2)
 | 
				
			||||||
 | 
					          (and (equal? head1 head2)
 | 
				
			||||||
 | 
					               (loop rest1 rest2))))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (special-form-lead symbol context)
 | 
				
			||||||
 | 
					  "If SYMBOL is a special form in the given CONTEXT, return its number of
 | 
				
			||||||
 | 
					arguments; otherwise return #f.  CONTEXT is a stack of symbols lexically
 | 
				
			||||||
 | 
					surrounding SYMBOL."
 | 
				
			||||||
 | 
					  (match (vhash-assq symbol %special-forms)
 | 
				
			||||||
 | 
					    (#f #f)
 | 
				
			||||||
 | 
					    ((_ . alist)
 | 
				
			||||||
 | 
					     (any (match-lambda
 | 
				
			||||||
 | 
					            ((prefix . level)
 | 
				
			||||||
 | 
					             (and (prefix? prefix context) (- level 1))))
 | 
				
			||||||
 | 
					          alist))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define (escaped-string str)
 | 
					(define (escaped-string str)
 | 
				
			||||||
  "Return STR with backslashes and double quotes escaped.  Everything else, in
 | 
					  "Return STR with backslashes and double quotes escaped.  Everything else, in
 | 
				
			||||||
| 
						 | 
					@ -192,8 +219,9 @@ Lists longer than LONG-LIST are written as one element per line."
 | 
				
			||||||
  (let loop ((indent indent)
 | 
					  (let loop ((indent indent)
 | 
				
			||||||
             (column indent)
 | 
					             (column indent)
 | 
				
			||||||
             (delimited? #t)                  ;true if comes after a delimiter
 | 
					             (delimited? #t)                  ;true if comes after a delimiter
 | 
				
			||||||
 | 
					             (context '())                    ;list of "parent" symbols
 | 
				
			||||||
             (obj obj))
 | 
					             (obj obj))
 | 
				
			||||||
    (define (print-sequence indent column lst delimited?)
 | 
					    (define (print-sequence context indent column lst delimited?)
 | 
				
			||||||
      (define long?
 | 
					      (define long?
 | 
				
			||||||
        (> (length lst) long-list))
 | 
					        (> (length lst) long-list))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -223,6 +251,7 @@ Lists longer than LONG-LIST are written as one element per line."
 | 
				
			||||||
                    (comment? item)
 | 
					                    (comment? item)
 | 
				
			||||||
                    (loop indent column
 | 
					                    (loop indent column
 | 
				
			||||||
                          (or newline? delimited?)
 | 
					                          (or newline? delimited?)
 | 
				
			||||||
 | 
					                          context
 | 
				
			||||||
                          item)))))))
 | 
					                          item)))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (define (sequence-would-protrude? indent lst)
 | 
					    (define (sequence-would-protrude? indent lst)
 | 
				
			||||||
| 
						 | 
					@ -243,6 +272,9 @@ Lists longer than LONG-LIST are written as one element per line."
 | 
				
			||||||
               #f))
 | 
					               #f))
 | 
				
			||||||
            lst))
 | 
					            lst))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (define (special-form? head)
 | 
				
			||||||
 | 
					      (special-form-lead head context))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (match obj
 | 
					    (match obj
 | 
				
			||||||
      ((? comment? comment)
 | 
					      ((? comment? comment)
 | 
				
			||||||
       (if (comment-margin? comment)
 | 
					       (if (comment-margin? comment)
 | 
				
			||||||
| 
						 | 
					@ -261,43 +293,44 @@ Lists longer than LONG-LIST are written as one element per line."
 | 
				
			||||||
      (('quote lst)
 | 
					      (('quote lst)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "'" port)
 | 
					       (display "'" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 1 2)) #t lst))
 | 
					       (loop indent (+ column (if delimited? 1 2)) #t context lst))
 | 
				
			||||||
      (('quasiquote lst)
 | 
					      (('quasiquote lst)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "`" port)
 | 
					       (display "`" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 1 2)) #t lst))
 | 
					       (loop indent (+ column (if delimited? 1 2)) #t context lst))
 | 
				
			||||||
      (('unquote lst)
 | 
					      (('unquote lst)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "," port)
 | 
					       (display "," port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 1 2)) #t lst))
 | 
					       (loop indent (+ column (if delimited? 1 2)) #t context lst))
 | 
				
			||||||
      (('unquote-splicing lst)
 | 
					      (('unquote-splicing lst)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display ",@" port)
 | 
					       (display ",@" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 2 3)) #t lst))
 | 
					       (loop indent (+ column (if delimited? 2 3)) #t context lst))
 | 
				
			||||||
      (('gexp lst)
 | 
					      (('gexp lst)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "#~" port)
 | 
					       (display "#~" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 2 3)) #t lst))
 | 
					       (loop indent (+ column (if delimited? 2 3)) #t context lst))
 | 
				
			||||||
      (('ungexp obj)
 | 
					      (('ungexp obj)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "#$" port)
 | 
					       (display "#$" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 2 3)) #t obj))
 | 
					       (loop indent (+ column (if delimited? 2 3)) #t context obj))
 | 
				
			||||||
      (('ungexp-native obj)
 | 
					      (('ungexp-native obj)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "#+" port)
 | 
					       (display "#+" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 2 3)) #t obj))
 | 
					       (loop indent (+ column (if delimited? 2 3)) #t context obj))
 | 
				
			||||||
      (('ungexp-splicing lst)
 | 
					      (('ungexp-splicing lst)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "#$@" port)
 | 
					       (display "#$@" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 3 4)) #t lst))
 | 
					       (loop indent (+ column (if delimited? 3 4)) #t context lst))
 | 
				
			||||||
      (('ungexp-native-splicing lst)
 | 
					      (('ungexp-native-splicing lst)
 | 
				
			||||||
       (unless delimited? (display " " port))
 | 
					       (unless delimited? (display " " port))
 | 
				
			||||||
       (display "#+@" port)
 | 
					       (display "#+@" port)
 | 
				
			||||||
       (loop indent (+ column (if delimited? 3 4)) #t lst))
 | 
					       (loop indent (+ column (if delimited? 3 4)) #t context lst))
 | 
				
			||||||
      (((? special-form? head) arguments ...)
 | 
					      (((? special-form? head) arguments ...)
 | 
				
			||||||
       ;; Special-case 'let', 'lambda', 'modify-inputs', etc. so the second
 | 
					       ;; Special-case 'let', 'lambda', 'modify-inputs', etc. so the second
 | 
				
			||||||
       ;; and following arguments are less indented.
 | 
					       ;; and following arguments are less indented.
 | 
				
			||||||
       (let* ((lead  (- (cdr (vhash-assq head %special-forms)) 1))
 | 
					       (let* ((lead    (special-form-lead head context))
 | 
				
			||||||
 | 
					              (context (cons head context))
 | 
				
			||||||
              (head    (symbol->string head))
 | 
					              (head    (symbol->string head))
 | 
				
			||||||
              (total   (length arguments)))
 | 
					              (total   (length arguments)))
 | 
				
			||||||
         (unless delimited? (display " " port))
 | 
					         (unless delimited? (display " " port))
 | 
				
			||||||
| 
						 | 
					@ -327,14 +360,14 @@ Lists longer than LONG-LIST are written as one element per line."
 | 
				
			||||||
                     (() column)
 | 
					                     (() column)
 | 
				
			||||||
                     ((head . tail)
 | 
					                     ((head . tail)
 | 
				
			||||||
                      (inner (- n 1) tail
 | 
					                      (inner (- n 1) tail
 | 
				
			||||||
                             (loop initial-indent
 | 
					                             (loop initial-indent column
 | 
				
			||||||
                                   column
 | 
					 | 
				
			||||||
                                   (= n lead)
 | 
					                                   (= n lead)
 | 
				
			||||||
 | 
					                                   context
 | 
				
			||||||
                                   head)))))))
 | 
					                                   head)))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           ;; Print the remaining arguments.
 | 
					           ;; Print the remaining arguments.
 | 
				
			||||||
           (let ((column (print-sequence
 | 
					           (let ((column (print-sequence
 | 
				
			||||||
                          indent new-column
 | 
					                          context indent new-column
 | 
				
			||||||
                          (drop arguments (min lead total))
 | 
					                          (drop arguments (min lead total))
 | 
				
			||||||
                          #t)))
 | 
					                          #t)))
 | 
				
			||||||
             (display ")" port)
 | 
					             (display ")" port)
 | 
				
			||||||
| 
						 | 
					@ -343,14 +376,15 @@ Lists longer than LONG-LIST are written as one element per line."
 | 
				
			||||||
       (let* ((overflow? (>= column max-width))
 | 
					       (let* ((overflow? (>= column max-width))
 | 
				
			||||||
              (column    (if overflow?
 | 
					              (column    (if overflow?
 | 
				
			||||||
                             (+ indent 1)
 | 
					                             (+ indent 1)
 | 
				
			||||||
                             (+ column (if delimited? 1 2)))))
 | 
					                             (+ column (if delimited? 1 2))))
 | 
				
			||||||
 | 
					              (context   (cons head context)))
 | 
				
			||||||
         (if overflow?
 | 
					         (if overflow?
 | 
				
			||||||
             (begin
 | 
					             (begin
 | 
				
			||||||
               (newline port)
 | 
					               (newline port)
 | 
				
			||||||
               (display (make-string indent #\space) port))
 | 
					               (display (make-string indent #\space) port))
 | 
				
			||||||
             (unless delimited? (display " " port)))
 | 
					             (unless delimited? (display " " port)))
 | 
				
			||||||
         (display "(" port)
 | 
					         (display "(" port)
 | 
				
			||||||
         (let* ((new-column (loop column column #t head))
 | 
					         (let* ((new-column (loop column column #t context head))
 | 
				
			||||||
                (indent (if (or (>= new-column max-width)
 | 
					                (indent (if (or (>= new-column max-width)
 | 
				
			||||||
                                (not (symbol? head))
 | 
					                                (not (symbol? head))
 | 
				
			||||||
                                (sequence-would-protrude?
 | 
					                                (sequence-would-protrude?
 | 
				
			||||||
| 
						 | 
					@ -358,7 +392,7 @@ Lists longer than LONG-LIST are written as one element per line."
 | 
				
			||||||
                            column
 | 
					                            column
 | 
				
			||||||
                            (+ new-column 1))))
 | 
					                            (+ new-column 1))))
 | 
				
			||||||
           (define column
 | 
					           (define column
 | 
				
			||||||
             (print-sequence indent new-column tail #f))
 | 
					             (print-sequence context indent new-column tail #f))
 | 
				
			||||||
           (display ")" port)
 | 
					           (display ")" port)
 | 
				
			||||||
           (+ column 1))))
 | 
					           (+ column 1))))
 | 
				
			||||||
      (_
 | 
					      (_
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -453,6 +453,18 @@ mnopqrstuvwxyz.\")"
 | 
				
			||||||
 \"abcdefghijklmnopqrstuvwxyz\")"
 | 
					 \"abcdefghijklmnopqrstuvwxyz\")"
 | 
				
			||||||
                   #:max-width 33)
 | 
					                   #:max-width 33)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(test-pretty-print "\
 | 
				
			||||||
 | 
					(modify-phases %standard-phases
 | 
				
			||||||
 | 
					  (replace 'build
 | 
				
			||||||
 | 
					    ;; Nicely indented in 'modify-phases' context.
 | 
				
			||||||
 | 
					    (lambda _
 | 
				
			||||||
 | 
					      #t)))")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(test-pretty-print "\
 | 
				
			||||||
 | 
					(modify-inputs inputs
 | 
				
			||||||
 | 
					  ;; Regular indentation for 'replace' here.
 | 
				
			||||||
 | 
					  (replace \"gmp\" gmp))")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(test-end)
 | 
					(test-end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
;; Local Variables:
 | 
					;; Local Variables:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Reference in a new issue