-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathkeyword.ss
75 lines (68 loc) · 2.56 KB
/
keyword.ss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#lang scheme/base
(require scheme/match
(only-in srfi/1 zip unzip2)
"base.ss"
"exn.ss")
; any ... -> any
;
; A version of keyword-apply that accepts arguments in a more humane order.
(define keyword-apply*
(match-lambda*
[(list (? procedure? proc) args ... rest)
; (listof any) (listof any) (listof any) (listof any) -> any
;
; Folds over a list of arguments, collecting keywords, values and other args.
;
; Arguments are:
; - args - unprocessed arguments to keyword-apply*;
; - key-accum - accumulator for keyword argument keywords;
; - val-accum - accumulator for keyword argument values;
; - arg-accum - accumulator for non-keyword arguments.
(define (expand args key-accum val-accum arg-accum)
(match args
; No rest argument:
[(list)
(finish key-accum
val-accum
arg-accum)]
; Final argument (i.e. rest argument):
[(list arg)
(if (keyword? rest)
(raise-exn exn:fail:contract
(format "keyword does not have a value: ~s" rest))
(expand null
key-accum
val-accum
(cons arg arg-accum)))]
; Well formed keyword argument:
[(list-rest (? keyword? key) val rest)
(if (keyword? val)
(raise-exn exn:fail:contract
(format "keyword does not have a value: ~s" key))
(expand rest
(cons key key-accum)
(cons val val-accum)
arg-accum))]
; Regular argument:
[(list-rest arg rest)
(expand rest
key-accum
val-accum
(cons arg arg-accum))]))
; (listof syntax) (listof syntax) (listof syntax) -> any
(define (finish keys vals args)
(define-values (sorted-keys sorted-vals)
(unzip2 (sort (zip keys vals)
(lambda (kvp1 kvp2)
(keyword<? (car kvp1) (car kvp2))))))
(keyword-apply proc
sorted-keys
sorted-vals
(reverse args)))
(if (or (null? rest) (pair? rest))
(expand (append args rest) null null null)
(raise-exn exn:fail:contract
(format "final argument must be a list: ~s" rest)))]))
; Provide statements -----------------------------
(provide/contract
[keyword-apply* (->* (procedure? any/c) () #:rest any/c any)])