Commit | Line | Data |
---|---|---|
e525e4e4 NI |
1 | ;;; Extensions to SRFI-9 |
2 | ||
d9e36897 | 3 | ;; Copyright (C) 2010, 2012 Free Software Foundation, Inc. |
e525e4e4 NI |
4 | ;; |
5 | ;; This library is free software; you can redistribute it and/or | |
6 | ;; modify it under the terms of the GNU Lesser General Public | |
7 | ;; License as published by the Free Software Foundation; either | |
8 | ;; version 3 of the License, or (at your option) any later version. | |
9 | ;; | |
10 | ;; This library is distributed in the hope that it will be useful, | |
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | ;; Lesser General Public License for more details. | |
14 | ;; | |
15 | ;; You should have received a copy of the GNU Lesser General Public | |
16 | ;; License along with this library; if not, write to the Free Software | |
17 | ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | ||
19 | ;;; Commentary: | |
20 | ||
21 | ;; Extensions to SRFI-9. Fully documented in the Guile Reference Manual. | |
22 | ||
23 | ;;; Code: | |
24 | ||
25 | (define-module (srfi srfi-9 gnu) | |
d9e36897 MW |
26 | #:use-module (srfi srfi-1) |
27 | #:export (set-record-type-printer! | |
28 | define-immutable-record-type | |
29 | set-field | |
30 | set-fields)) | |
e525e4e4 | 31 | |
167510bc NI |
32 | (define (set-record-type-printer! type thunk) |
33 | "Set a custom printer THUNK for TYPE." | |
6942d864 | 34 | (struct-set! type vtable-index-printer thunk)) |
d9e36897 MW |
35 | |
36 | (define-syntax-rule (define-immutable-record-type name ctor pred fields ...) | |
37 | ((@@ (srfi srfi-9) %define-record-type) #t name ctor pred fields ...)) | |
38 | ||
39 | (define-syntax-rule (set-field (getter ...) s expr) | |
40 | (%set-fields #t (set-field (getter ...) s expr) () | |
41 | s ((getter ...) expr))) | |
42 | ||
43 | (define-syntax-rule (set-fields s . rest) | |
44 | (%set-fields #t (set-fields s . rest) () | |
45 | s . rest)) | |
46 | ||
47 | ;; | |
48 | ;; collate-set-field-specs is a helper for %set-fields | |
49 | ;; thats combines all specs with the same head together. | |
50 | ;; | |
51 | ;; For example: | |
52 | ;; | |
53 | ;; SPECS: (((a b c) expr1) | |
54 | ;; ((a d) expr2) | |
55 | ;; ((b c) expr3) | |
56 | ;; ((c) expr4)) | |
57 | ;; | |
58 | ;; RESULT: ((a ((b c) expr1) | |
59 | ;; ((d) expr2)) | |
60 | ;; (b ((c) expr3)) | |
61 | ;; (c (() expr4))) | |
62 | ;; | |
63 | (define (collate-set-field-specs specs) | |
64 | (define (insert head tail expr result) | |
65 | (cond ((find (lambda (tree) | |
66 | (free-identifier=? head (car tree))) | |
67 | result) | |
68 | => (lambda (tree) | |
69 | `((,head (,tail ,expr) | |
70 | ,@(cdr tree)) | |
71 | ,@(delq tree result)))) | |
72 | (else `((,head (,tail ,expr)) | |
73 | ,@result)))) | |
74 | (with-syntax (((((head . tail) expr) ...) specs)) | |
75 | (fold insert '() #'(head ...) #'(tail ...) #'(expr ...)))) | |
76 | ||
77 | (define-syntax %set-fields-unknown-getter | |
78 | (lambda (x) | |
79 | (syntax-case x () | |
80 | ((_ orig-form getter) | |
81 | (syntax-violation 'set-fields "unknown getter" #'orig-form #'getter))))) | |
82 | ||
83 | (define-syntax %set-fields | |
84 | (lambda (x) | |
85 | (with-syntax ((getter-type #'(@@ (srfi srfi-9) getter-type)) | |
86 | (getter-index #'(@@ (srfi srfi-9) getter-index)) | |
87 | (getter-copier #'(@@ (srfi srfi-9) getter-copier))) | |
88 | (syntax-case x () | |
89 | ((_ check? orig-form (path-so-far ...) | |
90 | s) | |
91 | #'s) | |
92 | ((_ check? orig-form (path-so-far ...) | |
93 | s (() e)) | |
94 | #'e) | |
95 | ((_ check? orig-form (path-so-far ...) | |
96 | struct-expr ((head . tail) expr) ...) | |
97 | (let ((collated-specs (collate-set-field-specs | |
98 | #'(((head . tail) expr) ...)))) | |
99 | (with-syntax ((getter (caar collated-specs))) | |
100 | (with-syntax ((err #'(%set-fields-unknown-getter | |
101 | orig-form getter))) | |
102 | #`(let ((s struct-expr)) | |
103 | ((getter-copier getter err) | |
104 | check? | |
105 | s | |
106 | #,@(map (lambda (spec) | |
107 | (with-syntax (((head (tail expr) ...) spec)) | |
108 | (with-syntax ((err #'(%set-fields-unknown-getter | |
109 | orig-form head))) | |
110 | #'(head (%set-fields | |
111 | check? | |
112 | orig-form | |
113 | (path-so-far ... head) | |
114 | (struct-ref s (getter-index head err)) | |
115 | (tail expr) ...))))) | |
116 | collated-specs))))))) | |
117 | ((_ check? orig-form (path-so-far ...) | |
118 | s (() e) (() e*) ...) | |
119 | (syntax-violation 'set-fields "duplicate field path" | |
120 | #'orig-form #'(path-so-far ...))) | |
121 | ((_ check? orig-form (path-so-far ...) | |
122 | s ((getter ...) expr) ...) | |
123 | (syntax-violation 'set-fields "one field path is a prefix of another" | |
124 | #'orig-form #'(path-so-far ...))) | |
125 | ((_ check? orig-form . rest) | |
126 | (syntax-violation 'set-fields "invalid syntax" #'orig-form)))))) |