aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Devos <maximedevos@telenet.be>2022-12-20 14:22:15 +0100
committerMaxime Devos <maximedevos@telenet.be>2022-12-20 14:22:15 +0100
commit908fa9dfbf95c292b6322935daededec1d8ab8a7 (patch)
tree3ff6d00d0f357be1e842d3070666706589242051
parent8e9c3b86b8895172bcd96a4375775c442b3b072d (diff)
downloadgnunet-scheme-908fa9dfbf95c292b6322935daededec1d8ab8a7.tar.gz
gnunet-scheme-908fa9dfbf95c292b6322935daededec1d8ab8a7.zip
Simplify structure analysis.
* NEWS: Mention new changes. * doc/network-structures.tm: Document new macros. * gnu/gnunet/cadet/client.scm: Use new macros. * gnu/gnunet/dht/client.scm: Likewise. * gnu/gnunet/fs/network.scm: Likewise. * gnu/gnunet/netstruct/syntactic.scm (r%,s%,analyse,define-analyser): New macros.
-rw-r--r--NEWS3
-rw-r--r--doc/network-structures.tm63
-rw-r--r--gnu/gnunet/cadet/client.scm42
-rw-r--r--gnu/gnunet/dht/client.scm114
-rw-r--r--gnu/gnunet/fs/network.scm24
-rw-r--r--gnu/gnunet/netstruct/syntactic.scm38
6 files changed, 174 insertions, 110 deletions
diff --git a/NEWS b/NEWS
index d1faba9..4e2a667 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,9 @@
12 in the manual. These tools have been used to reduce duplication between 12 in the manual. These tools have been used to reduce duplication between
13 client code of different services, so tests targeting a single service 13 client code of different services, so tests targeting a single service
14 automatically also test the other services a bit. 14 automatically also test the other services a bit.
15 - New macro 'analyse' and 'define-analyser', to make using read% and select%
16 less tedious when the type and slice remains the same. Also, by using the
17 new macros, the code base should now be a bit more readible.
15** Bugfixes 18** Bugfixes
16 - A potential (but unverified) bug with automatic collection is fixed -- 19 - A potential (but unverified) bug with automatic collection is fixed --
17 previously, if DHT garbage was found multiple times within a single 20 previously, if DHT garbage was found multiple times within a single
diff --git a/doc/network-structures.tm b/doc/network-structures.tm
index d52b3b2..ebda27a 100644
--- a/doc/network-structures.tm
+++ b/doc/network-structures.tm
@@ -177,6 +177,8 @@
177 The fields can also be read: 177 The fields can also be read:
178 178
179 <\scm-code> 179 <\scm-code>
180 ;; This example is simplified later!
181
180 (read% /:msg:nse:estimate/example '(header size) message) ; 12 182 (read% /:msg:nse:estimate/example '(header size) message) ; 12
181 183
182 (read% /:msg:nse:estimate/example '(header type) message) ; 165 184 (read% /:msg:nse:estimate/example '(header type) message) ; 165
@@ -184,6 +186,67 @@
184 (read% /:msg:nse:estimate/example '(size-estimate) message) ; 19.2 186 (read% /:msg:nse:estimate/example '(size-estimate) message) ; 19.2
185 </scm-code> 187 </scm-code>
186 188
189 Repeating the message type and the slice can be repetitive, so <scm|(gnu
190 gnunet netstruct syntactic)> has a macro to avoid that:
191
192 <\explain>
193 <scm|(analyse <var|type> <var|message> <var|body>
194 <text-dots>)><index|analyse>
195 <|explain>
196 Expand to <scm|<var|body ...>> \ in a context where the syntax parameters
197 <scm|r%> and <scm|s%> (from <scm|(gnu gnunet netstruct syntactic)>) are
198 bound to macros with the following interface:
199
200 <\description>
201 <item*|<scm|(r% <var|field> ...)><index|r%>>Read the field
202 <scm|(<var|field> ...)> of <var|message>. This is to be understood as a
203 literal list, not as a procedure invocation \U neither the list nor
204 <var|field> <text-dots> is evaluated.
205
206 <item*|<scm|(s% <var|field> ...)><index|s%>>Select the field
207 <scm|(field ...)> of <var|message>, where <scm|(field ...)> is
208 interpreted the same way as for <scm|r%>.
209 </description>
210
211 <var|type> and <var|message> can currently be evaluated multiple times.
212 These macros <scm|r%> and <scm|s%> can only be used inside a
213 <scm|analyse> or <scm|define-analyser> construct; in other contexts an
214 exception is raised.
215 </explain>
216
217 Using this macro, the previous example can be simplified to: '
218
219 <\scm-code>
220 ;; 'pk' is for printing the value
221
222 (analyse /:msg:nse:estimate/example message
223
224 \ \ \ \ \ \ \ \ \ (pk (r% header size)) ; 12
225
226 \ \ \ \ \ \ \ \ \ (pk (r% header type)) ; 165
227
228 \ \ \ \ \ \ \ \ \ (pk (r% size-estimate))) ; 19.2
229 </scm-code>
230
231 For very simple 'analysis' procedures, the macro
232 <scm|define-analyser><index|define-analyser> can be useful:
233
234 <\scm-code>
235 ;; 'pk' is for printing the value
236
237 (define-analyser analyse-example /:msg:nse:estimate/example
238
239 \ \ "Put a docstring here"
240
241 \ \ (pk (r% header size)) ; 12
242
243 \ \ (pk (r% header type)) ; 165
244
245 \ \ (pk (r% size-estimate)))
246
247 (analyse-example [...])
248 </scm-code>
249
187 <section|Primitive types> 250 <section|Primitive types>
188 251
189 There are a number of pre-defined types.<space|1em>First, there is 252 There are a number of pre-defined types.<space|1em>First, there is
diff --git a/gnu/gnunet/cadet/client.scm b/gnu/gnunet/cadet/client.scm
index c37b908..50118fe 100644
--- a/gnu/gnunet/cadet/client.scm
+++ b/gnu/gnunet/cadet/client.scm
@@ -77,7 +77,7 @@
77 (only (gnu gnunet mq) 77 (only (gnu gnunet mq)
78 make-message-queue inject-message!) 78 make-message-queue inject-message!)
79 (only (gnu gnunet netstruct syntactic) 79 (only (gnu gnunet netstruct syntactic)
80 sizeof select read% set%!) 80 sizeof select read% set%! r% s% define-analyser analyse)
81 (only (gnu gnunet utils bv-slice) 81 (only (gnu gnunet utils bv-slice)
82 make-slice/read-write slice-copy/read-only slice-length 82 make-slice/read-write slice-copy/read-only slice-length
83 slice-copy! slice-slice) 83 slice-copy! slice-slice)
@@ -412,20 +412,13 @@ the CADET addresss @var{cadet-address}, using the channel number
412 mq (construct-local-channel-create 412 mq (construct-local-channel-create
413 (channel-address channel) (channel-channel-number channel)))) 413 (channel-address channel) (channel-channel-number channel))))
414 414
415 (define (analyse-local-channel-create message) 415 (define-analyser analyse-local-channel-create
416 /:msg:cadet:local:channel:create
416 "Return the CADET address, channel number and options corresponding to 417 "Return the CADET address, channel number and options corresponding to
417the @code{/:msg:cadet:channel:create} message @var{message}." 418the @code{/:msg:cadet:channel:create} message @var{message}."
418 (define-syntax read* 419 (values (make-cadet-address (s% peer) (s% port))
419 (cut-syntax read% /:msg:cadet:local:channel:create <> message)) 420 (r% channel-number)
420 (define-syntax select* 421 (r% options)))
421 (cut-syntax select /:msg:cadet:local:channel:create <> message))
422 (let^ ((! channel-number (read* '(channel-number)))
423 (! peer (select* '(peer)))
424 (! port (select* '(port)))
425 (! channel-number (read* '(channel-number)))
426 (! options (read* '(options)))
427 (! address (make-cadet-address peer port)))
428 (values address channel-number options)))
429 422
430 (define (construct-local-channel-destroy channel-number) 423 (define (construct-local-channel-destroy channel-number)
431 "Create a @code{/:msg:cadet:channel:destroy} message for closing the 424 "Create a @code{/:msg:cadet:channel:destroy} message for closing the
@@ -441,10 +434,11 @@ CADET channel with channel number @var{channel-number}."
441 (set* '(channel-number) channel-number) 434 (set* '(channel-number) channel-number)
442 s) 435 s)
443 436
444 (define (analyse-local-channel-destroy message) 437 (define-analyser analyse-local-channel-destroy
438 /:msg:cadet:local:channel:destroy
445 "Return the channel number corresponding to the 439 "Return the channel number corresponding to the
446@code{/:msg:cadet:local:channel:destroy} message @var{message}." 440@code{/:msg:cadet:local:channel:destroy} message @var{message}."
447 (read% /:msg:cadet:local:channel:destroy '(channel-number) message)) 441 (r% channel-number))
448 442
449 ;; TODO: determine maximum length 443 ;; TODO: determine maximum length
450 (define %max-cadet-message-size 444 (define %max-cadet-message-size
@@ -474,14 +468,11 @@ CADET channel with channel number @var{channel-number}."
474in the @code{/:msg:cadet:local:data} message @var{message}." 468in the @code{/:msg:cadet:local:data} message @var{message}."
475 (define header 469 (define header
476 (slice-slice message 0 (sizeof /:msg:cadet:local:data '()))) 470 (slice-slice message 0 (sizeof /:msg:cadet:local:data '())))
477 (define-syntax read* 471 (analyse /:msg:cadet:local:data header
478 (cut-syntax read% /:msg:cadet:local:data <> header)) 472 (values (r% channel-number)
479 (define-syntax select* 473 (r% priority-preference)
480 (cut-syntax select /:msg:cadet:local:data <> header)) 474 (slice-slice message
481 (values (read* '(channel-number)) 475 (sizeof /:msg:cadet:local:data '())))))
482 (read* '(priority-preference))
483 (slice-slice message
484 (sizeof /:msg:cadet:local:data '()))))
485 476
486 (define (construct-local-acknowledgement channel-number) 477 (define (construct-local-acknowledgement channel-number)
487 "Create a @code{/:msg:cadet:local:acknowledgement} message, 478 "Create a @code{/:msg:cadet:local:acknowledgement} message,
@@ -498,10 +489,11 @@ identified by @var{channel-number}."
498 (set* '(client-channel-number) channel-number) 489 (set* '(client-channel-number) channel-number)
499 s) 490 s)
500 491
501 (define (analyse-local-acknowledgement message) 492 (define-analyser analyse-local-acknowledgement
493 /:msg:cadet:local:acknowledgement
502 "Return the channel number in the @code{/:msg:cadet:local:data} 494 "Return the channel number in the @code{/:msg:cadet:local:data}
503message @var{message}." 495message @var{message}."
504 (read% /:msg:cadet:local:acknowledgement '(client-channel-number) message)) 496 (r% client-channel-number))
505 497
506 (define (stub . foo) 498 (define (stub . foo)
507 (error "todo")) 499 (error "todo"))
diff --git a/gnu/gnunet/dht/client.scm b/gnu/gnunet/dht/client.scm
index bfc4bcc..047ca6d 100644
--- a/gnu/gnunet/dht/client.scm
+++ b/gnu/gnunet/dht/client.scm
@@ -88,7 +88,8 @@
88 run-loop spawn-server-loop) 88 run-loop spawn-server-loop)
89 (only (guile) 89 (only (guile)
90 pk define-syntax-rule define* lambda* error 90 pk define-syntax-rule define* lambda* error
91 ->bool and=>) 91 ->bool and=> define-syntax-parameter syntax
92 syntax-parameterize)
92 (only (ice-9 atomic) 93 (only (ice-9 atomic)
93 make-atomic-box) 94 make-atomic-box)
94 (only (ice-9 match) 95 (only (ice-9 match)
@@ -106,7 +107,7 @@
106 (only (gnu gnunet message protocols) 107 (only (gnu gnunet message protocols)
107 message-type) 108 message-type)
108 (only (gnu gnunet netstruct syntactic) 109 (only (gnu gnunet netstruct syntactic)
109 read% sizeof set%! select) 110 read% sizeof set%! select r% s% analyse define-analyser)
110 (only (gnu gnunet utils bv-slice) 111 (only (gnu gnunet utils bv-slice)
111 slice-length slice/read-only make-slice/read-write slice-copy! 112 slice-length slice/read-only make-slice/read-write slice-copy!
112 slice-slice verify-slice-readable slice-copy/read-write 113 slice-slice verify-slice-readable slice-copy/read-write
@@ -501,83 +502,66 @@ result object @var{search-result}, with @var{unique-id} as ‘unique id’"
501 (slice-copy! value rest) 502 (slice-copy! value rest)
502 message)) 503 message))
503 504
504 (define (analyse-client-get message) 505 (define-analyser analyse-client-get /:msg:dht:client:get
505 "Return the query object, the unique id and the options corresponding to 506 "Return the query object, the unique id and the options corresponding to
506the @code{/:msg:dht:client:result} message @var{message}. Xqueries are 507the @code{/:msg:dht:client:result} message @var{message}. Xqueries are
507currently unsupported." 508currently unsupported."
508 (let^ ((! type (read% /:msg:dht:client:get '(type) message)) 509 (values (make-query (r% type) (make-hashcode:512/share (s% key))
509 (! key (make-hashcode:512/share 510 #:desired-replication-level
510 (select /:msg:dht:client:get '(key) message))) 511 (r% desired-replication-level))
511 (! desired-replication-level 512 (r% unique-id)
512 (read% /:msg:dht:client:get '(desired-replication-level) message)) 513 (r% options)))
513 (! unique-id (read% /:msg:dht:client:get '(unique-id) message)) 514
514 (! options (read% /:msg:dht:client:get '(options) message)) 515 (define-analyser analyse-client-get-stop /:msg:dht:client:get:stop
515 (! query
516 (make-query type key #:desired-replication-level
517 desired-replication-level)))
518 (values query unique-id options)))
519
520 (define (analyse-client-get-stop message)
521 "Return the unique id and the key corresponding to the 516 "Return the unique id and the key corresponding to the
522@code{/:msg:dht:client:stop} message @var{message}." 517@code{/:msg:dht:client:stop} message @var{message}."
523 (values (read% /:msg:dht:client:get:stop '(unique-id) message) 518 (values (r% unique-id) (s% key)))
524 (select /:msg:dht:client:get:stop '(key) message)))
525 519
526 (define (analyse-client-put message) 520 (define (analyse-client-put message)
527 "Return the insertion object and options corresponding to the 521 "Return the insertion object and options corresponding to the
528@code{/:msg:dht:client:put} message @var{message}." 522@code{/:msg:dht:client:put} message @var{message}."
529 (let^ ((! header (slice-slice message 0 (sizeof /:msg:dht:client:put '()))) 523 (define header (slice-slice message 0 (sizeof /:msg:dht:client:put '())))
530 (! type (read% /:msg:dht:client:put '(type) header)) 524 (define value (slice-slice message (sizeof /:msg:dht:client:put '())))
531 (! key 525 (analyse /:msg:dht:client:put header
532 (make-hashcode:512/share 526 (values
533 (select /:msg:dht:client:put '(key) header))) 527 (datum->insertion
534 (! value (slice-slice message (sizeof /:msg:dht:client:put '()))) 528 (make-datum
535 (! desired-replication-level 529 (r% type)
536 (read% /:msg:dht:client:put '(desired-replication-level) header)) 530 (make-hashcode:512/share (s% key))
537 (! expiration 531 value
538 (read% /:msg:dht:client:put '(expiration) header)) 532 #:expiration (r% expiration))
539 (! options 533 #:desired-replication-level (r% desired-replication-level))
540 (read% /:msg:dht:client:put '(option) header)) 534 (r% option))))
541 (! datum (make-datum type key value #:expiration expiration))
542 (! insertion
543 (datum->insertion datum #:desired-replication-level
544 desired-replication-level)))
545 (values insertion options)))
546 535
547 (define (analyse-client-result message) 536 (define (analyse-client-result message)
548 "Return search result object and unique id for the 537 "Return search result object and unique id for the
549@code{/:msg:dht:client:result} message @var{message}." 538@code{/:msg:dht:client:result} message @var{message}."
550 (let^ ((! message (slice/read-only message)) 539 (define message* (slice/read-only message))
551 (! size/header (sizeof /:msg:dht:client:result '())) 540 (define size/header (sizeof /:msg:dht:client:result '()))
552 (! header (slice-slice message 0 size/header)) 541 (define header (slice-slice message* 0 size/header))
553 (! rest (slice/read-only message size/header)) 542 (define rest (slice-slice message* size/header))
554 (! put-path-length 543 (define size/path-element (sizeof /dht:path-element '()))
555 (read% /:msg:dht:client:result '(put-path-length) header)) 544 (analyse
556 (! get-path-length 545 /:msg:dht:client:result
557 (read% /:msg:dht:client:result '(get-path-length) header)) 546 header
558 (! size/path-element (sizeof /dht:path-element '())) 547 (values (datum->search-result
559 (! put-path
560 (slice-slice rest 0 (* size/path-element put-path-length)))
561 (! get-path
562 (slice-slice rest (* size/path-element put-path-length)
563 (* size/path-element get-path-length)))
564 (! value
565 (slice-slice rest (* (sizeof /dht:path-element '())
566 (+ put-path-length get-path-length))))
567 (! datum
568 (make-datum 548 (make-datum
569 (read% /:msg:dht:client:result '(type) header) 549 (r% type)
570 (make-hashcode:512/share 550 (make-hashcode:512/share (s% key))
571 (select /:msg:dht:client:result '(key) header)) 551 ;; 'value'
572 value 552 (slice-slice rest
573 #:expiration 553 (* size/path-element
574 (read% /:msg:dht:client:result '(expiration) header))) 554 (+ (r% put-path-length)
575 (! search-result 555 (r% get-path-length))))
576 (datum->search-result 556 #:expiration (r% expiration))
577 datum #:get-path get-path #:put-path put-path)) 557 #:get-path
578 (! unique-id (read% /:msg:dht:client:result '(unique-id) header))) 558 (slice-slice rest
579 (values search-result unique-id))) 559 (* size/path-element (r% put-path-length))
580 560 (* size/path-element (r% get-path-length)))
561 #:put-path
562 (slice-slice rest 0
563 (* size/path-element (r% put-path-length))))
564 (r% unique-id))))
581 565
582 566
583 ;; New operations are communicated to the main event loop 567 ;; New operations are communicated to the main event loop
diff --git a/gnu/gnunet/fs/network.scm b/gnu/gnunet/fs/network.scm
index 4b3b01c..65648a8 100644
--- a/gnu/gnunet/fs/network.scm
+++ b/gnu/gnunet/fs/network.scm
@@ -24,7 +24,6 @@
24 (only (guile) begin define*) 24 (only (guile) begin define*)
25 (only (gnu gnunet utils bv-slice) 25 (only (gnu gnunet utils bv-slice)
26 make-slice/read-write slice-copy! slice-length ) 26 make-slice/read-write slice-copy! slice-length )
27 (only (gnu gnunet utils hat-let) let^)
28 (only (gnu extractor enum) value->index symbol-value) 27 (only (gnu extractor enum) value->index symbol-value)
29 (only (gnu gnunet message protocols) message-type) 28 (only (gnu gnunet message protocols) message-type)
30 (only (gnu gnunet fs struct) 29 (only (gnu gnunet fs struct)
@@ -37,7 +36,7 @@
37 hashcode:512->slice) 36 hashcode:512->slice)
38 (only (gnu gnunet utils cut-syntax) cut-syntax) 37 (only (gnu gnunet utils cut-syntax) cut-syntax)
39 (only (gnu gnunet netstruct syntactic) 38 (only (gnu gnunet netstruct syntactic)
40 set%! sizeof select read%)) 39 set%! sizeof select r% s% define-analyser))
41 (begin 40 (begin
42 ;; GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT, 41 ;; GNUNET_SIGNATURE_PURPOSE_PEER_PLACEMENT,
43 ;; (see gnunet-signatures/gnunet_signatures.rst) 42 ;; (see gnunet-signatures/gnunet_signatures.rst)
@@ -72,19 +71,12 @@ expiring at @var{expiration-time} (TODO type), for @var{purpose}
72 (set%!* '(file-length) file-length) 71 (set%!* '(file-length) file-length)
73 s) 72 s)
74 73
75 (define (analyse-request-loc-signature message) 74 (define-analyser analyse-request-loc-signature
76 "Return the file length, content hash key, expiration time (TODO type) 75 /:msg:fs:request-loc-signature
76 "Return the file length, content hash key, expiration time (TODO type)
77and signature purpose corresponding to the @code{/:msg:fs:request-loc-signature} 77and signature purpose corresponding to the @code{/:msg:fs:request-loc-signature}
78message @var{message}." 78message @var{message}."
79 (let^ ((! file-length 79 (values (r% file-length)
80 (read% /:msg:fs:request-loc-signature '(file-length) message)) 80 (make-content-hash-key/share (s% content-hash-key))
81 (! content-hash-key 81 (r% expiration-time)
82 (make-content-hash-key/share 82 (r% purpose)))))
83 (select /:msg:fs:request-loc-signature
84 '(content-hash-key) message)))
85 (! expiration-time
86 (read% /:msg:fs:request-loc-signature
87 '(expiration-time) message))
88 (! purpose
89 (read% /:msg:fs:request-loc-signature '(purpose) message)))
90 (values file-length content-hash-key expiration-time purpose)))))
diff --git a/gnu/gnunet/netstruct/syntactic.scm b/gnu/gnunet/netstruct/syntactic.scm
index ff388af..fe54165 100644
--- a/gnu/gnunet/netstruct/syntactic.scm
+++ b/gnu/gnunet/netstruct/syntactic.scm
@@ -1,6 +1,6 @@
1;#!r6rs 1;#!r6rs
2;; This file is part of scheme-GNUnet, a partial Scheme port of GNUnet. 2;; This file is part of scheme-GNUnet, a partial Scheme port of GNUnet.
3;; Copyright (C) 2020, 2021 GNUnet e.V. 3;; Copyright © 2020--2022 GNUnet e.V.
4;; 4;;
5;; scheme-GNUnet is free software: you can redistribute it and/or modify it 5;; scheme-GNUnet is free software: you can redistribute it and/or modify it
6;; under the terms of the GNU Affero General Public License as published 6;; under the terms of the GNU Affero General Public License as published
@@ -21,7 +21,8 @@
21;; some checks and inlining during expansion. 21;; some checks and inlining during expansion.
22(define-library (gnu gnunet netstruct syntactic) 22(define-library (gnu gnunet netstruct syntactic)
23 (export sizeof offsetof select read% set%! 23 (export sizeof offsetof select read% set%!
24 structure/packed define-type) 24 structure/packed define-type
25 r% s% analyse define-analyser)
25 (import (rnrs base) 26 (import (rnrs base)
26 (rnrs control) 27 (rnrs control)
27 (only (rnrs exceptions) 28 (only (rnrs exceptions)
@@ -33,7 +34,8 @@
33 newline 34 newline
34 compose 35 compose
35 call-with-prompt abort-to-prompt make-prompt-tag 36 call-with-prompt abort-to-prompt make-prompt-tag
36 resolve-module module-ref) 37 resolve-module module-ref
38 define-syntax-parameter syntax-parameterize)
37 (only (system syntax) syntax-local-binding) 39 (only (system syntax) syntax-local-binding)
38 (gnu gnunet utils bv-slice) 40 (gnu gnunet utils bv-slice)
39 (only (srfi srfi-1) span assq filter-map concatenate) 41 (only (srfi srfi-1) span assq filter-map concatenate)
@@ -428,4 +430,32 @@ with some inlining where possible."
428 #`(#,(or (writer-syntax ns) 430 #`(#,(or (writer-syntax ns)
429 (not-inlinable 431 (not-inlinable
430 #'(p@set%! type fields slice value))) 432 #'(p@set%! type fields slice value)))
431 #,sl))))))))))) 433 #,sl)))))))))
434
435
436 ;; Documentation is in the manual.
437 (define-syntax-parameter r% ; read field
438 (lambda (stx)
439 (syntax-violation 'r%
440 "r% used outside of a 'analyzer' construct"
441 stx)))
442 (define-syntax-parameter s% ; select field
443 (lambda (stx)
444 (syntax-violation 's%
445 "s% used outside of a 'analyzer' construct"
446 stx)))
447 (define-syntax analyse
448 (syntax-rules ()
449 ((_ type message body ...)
450 (syntax-parameterize
451 ((r% (syntax-rules ()
452 ((_ . fields) (read% type 'fields message))))
453 (s% (syntax-rules ()
454 ((_ . fields) (select type 'fields message)))))
455 body ...))))
456 (define-syntax define-analyser
457 (syntax-rules ()
458 ((_ name type docstring body ...)
459 (define (name message)
460 docstring
461 (analyse type message body ...)))))))