• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

drym-org / qi / #201

18 Sep 2025 07:59PM UTC coverage: 93.412% (-0.003%) from 93.415%
#201

push

cover

countvajhula
drop Racket BC from the test matrix

This doesn't drop support for those versions, just that BC stable is
no longer distributed.

2127 of 2277 relevant lines covered (93.41%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.41
/qi-lib/flow/extended/expander.rkt
1
#lang racket/base
1✔
2

3
(provide (for-syntax qi-macro
1✔
4
                     closed-floe)
1✔
5
         (for-space qi
1✔
6
                    (all-defined-out)
1✔
7
                    (rename-out [ground ⏚]
1✔
8
                                [thread ~>]
1✔
9
                                [relay ==]
1✔
10
                                [tee -<]
1✔
11
                                [amp ><]
1✔
12
                                [sep △]
1✔
13
                                [collect ▽]
1✔
14
                                [NOT !])))
1✔
15

16
(require syntax-spec-v3
1✔
17
         "../space.rkt"
1✔
18
         (for-syntax "../aux-syntax.rkt"
1✔
19
                     "syntax.rkt"
1✔
20
                     racket/base
1✔
21
                     syntax/parse
1✔
22
                     "../../private/util.rkt"))
1✔
23

24
#|
25
This module implements the Qi expander using Syntax Spec.  Here, we
26
notate the core language grammar from which Syntax Spec infers and
27
constructs an appropriate expander.  As part of this, we annotate the
28
grammar with binding scope rules (e.g. for the `as` form) which allows
29
the expander to propagate scope in accordance with these specified
30
rules and also raise errors at compile time when identifiers used are
31
unbound.
32

33
In addition to the core language grammar and scoping rules, we also
34
specify a few ad hoc expansion rules here (via Syntax Spec "rewrite
35
productions") in order to expand surface syntax that may not be neatly
36
expressible as a macro to an appropriate use of a core form. Such
37
rules are necessary so that the core language can be uniformly
38
expressed in terms of such prefix forms (it's similar to the Racket
39
core language's use of #%app, etc.).
40
|#
41

42
(syntax-spec
1✔
43

44
  ;; Declare a compile-time datatype by which qi macros may
45
  ;; be identified.
46
  (extension-class qi-macro
1✔
47
                   #:binding-space qi)
1✔
48

49
  (nonterminal closed-floe
1✔
50
    #:description "a flow expression"
1✔
51

52
    f:floe
1✔
53
    #:binding (nest f []))
1✔
54

55
  ;; "floe" stands for "FLOw Expression" and is a _nonterminal_ that
56
  ;; expresses valid syntax for Qi expressions. It's analogous
57
  ;; to `expr` for Racket expressions (e.g. as used in syntax-parse).
58
  ;; Not to be confused with `flow` which is a Racket _form_
59
  ;; that extends Racket syntax by introducing a `floe` position
60
  ;; whose expansion is specified here.
61
  (nonterminal/nesting floe (nested)
1✔
62
    #:description "a flow expression"
1✔
63
    #:allow-extension qi-macro
1✔
64
    #:binding-space qi
1✔
65

66
    (as v:racket-var ...+)
1✔
67
    #:binding (scope (bind v) ... nested)
1✔
68

69
    (thread f:floe ...)
1✔
70
    #:binding (nest f ... nested)
1✔
71

72
    (tee f:floe ...)
1✔
73
    #:binding (nest f ... nested)
1✔
74
    tee
1✔
75
    ;; Note: `#:binding nested` is the implicit binding rule here
76

77
    (relay f:floe ...)
1✔
78
    #:binding (nest f ... nested)
1✔
79
    relay
1✔
80

81
    ;; [f nested] is the implicit binding rule
82
    ;; anything not mentioned (e.g. nested) is treated as a
83
    ;; subexpression that's not in any scope
84
    ;; Note: once a nonterminal is chosen, it doesn't backtrack
85
    ;; to consider alternatives
86

87
    (gen e:racket-expr ...)
1✔
88
    ;; Ad hoc expansion rule to allow _ to be used in application
89
    ;; position in a template.
90
    ;; Without it, (_ v ...) would be treated as an error since
91
    ;; _ is an unrelated form of the core language having different
92
    ;; semantics. The expander would assume it is a syntax error
93
    ;; from that perspective.
94
    (~> ((~literal _) arg ...) #'(#%fine-template (_ arg ...)))
1✔
95
    _
1✔
96
    ground
1✔
97
    amp
1✔
98
    (amp f:closed-floe)
1✔
99
    (~>/form (amp f0:clause f:clause ...)
1✔
100
             ;; potentially pull out as a phase 1 function
101
             ;; just a stopgap until better error messages
102
             (report-syntax-error this-syntax
1✔
103
               "(>< flo)"
1✔
104
               "amp expects a single flow specification, but it received many."))
1✔
105
    pass
1✔
106
    (pass f:closed-floe)
1✔
107
    sep
1✔
108
    (sep f:closed-floe)
1✔
109
    collect
1✔
110
    NOT
1✔
111
    XOR
1✔
112
    (and f:closed-floe ...)
1✔
113
    (or f:closed-floe ...)
1✔
114
    (not f:floe)
1✔
115
    #:binding (nest f nested)
1✔
116
    (all f:closed-floe)
1✔
117
    (any f:closed-floe)
1✔
118
    (select n:number ...)
1✔
119
    (~>/form (select arg ...)
1✔
120
             (report-syntax-error this-syntax
1✔
121
               "(select <number> ...)"))
1✔
122
    (block n:number ...)
1✔
123
    (~>/form (block arg ...)
1✔
124
             (report-syntax-error this-syntax
1✔
125
               "(block <number> ...)"))
1✔
126
    (fanout n:number)
1✔
127
    (fanout n:racket-expr)
1✔
128
    fanout
1✔
129
    (group n:racket-expr e1:floe e2:floe)
1✔
130
    #:binding (nest e1 e2 nested)
1✔
131
    group
1✔
132
    (~>/form (group arg ...)
1✔
133
             (report-syntax-error this-syntax
1✔
134
               "(group <number> <selection flow> <remainder flow>)"))
1✔
135
    (if consequent:closed-floe
1✔
136
        alternative:closed-floe)
1✔
137
    (if condition:floe
1✔
138
        consequent:closed-floe
1✔
139
        alternative:closed-floe)
1✔
140
    #:binding (nest condition [consequent alternative nested])
1✔
141
    (sieve condition:closed-floe
1✔
142
           sonex:closed-floe
1✔
143
           ronex:closed-floe)
1✔
144
    sieve
1✔
145
    (~>/form (sieve arg ...)
1✔
146
             (report-syntax-error this-syntax
1✔
147
               "(sieve <predicate flow> <selection flow> <remainder flow>)"))
1✔
148
    (partition)
1✔
149
    (partition [cond:closed-floe body:closed-floe] ...+)
1✔
150
    (try flo:closed-floe
1✔
151
      [error-condition-flo:closed-floe error-handler-flo:closed-floe]
1✔
152
      ...+)
1✔
153
    (~>/form (try arg ...)
1✔
154
             (report-syntax-error this-syntax
1✔
155
               "(try <flo> [error-predicate-flo error-handler-flo] ...)"))
1✔
156
    >>
1✔
157
    (>> fn:closed-floe init:closed-floe)
1✔
158
    (>> fn:closed-floe)
1✔
159
    <<
1✔
160
    (<< fn:closed-floe init:closed-floe)
1✔
161
    (<< fn:closed-floe)
1✔
162
    (feedback ((~datum while) tilex:closed-floe)
1✔
163
              ((~datum then) thenex:closed-floe)
1✔
164
              onex:closed-floe)
1✔
165
    (feedback ((~datum while) tilex:closed-floe)
1✔
166
              ((~datum then) thenex:closed-floe))
1✔
167
    (feedback ((~datum while) tilex:closed-floe) onex:closed-floe)
1✔
168
    (feedback ((~datum while) tilex:closed-floe))
1✔
169
    (feedback n:racket-expr
1✔
170
              ((~datum then) thenex:closed-floe)
1✔
171
              onex:closed-floe)
1✔
172
    (feedback n:racket-expr
1✔
173
              ((~datum then) thenex:closed-floe))
1✔
174
    (feedback n:racket-expr onex:closed-floe)
1✔
175
    (feedback onex:closed-floe)
1✔
176
    feedback
1✔
177
    (loop pred:closed-floe mapex:closed-floe combex:closed-floe retex:closed-floe)
1✔
178
    (loop pred:closed-floe mapex:closed-floe combex:closed-floe)
1✔
179
    (loop pred:closed-floe mapex:closed-floe)
1✔
180
    (loop mapex:closed-floe)
1✔
181
    loop
1✔
182
    (loop2 pred:closed-floe mapex:closed-floe combex:closed-floe)
1✔
183
    appleye
1✔
184
    (~> (~literal apply) #'appleye)
1✔
185
    clos
1✔
186
    (clos onex:closed-floe)
1✔
187
    (esc ex:racket-expr)
1✔
188
    (~> ((~literal lambda) e ...)
1✔
189
        #'(esc (lambda e ...)))
1✔
190
    (~> ((~literal λ) e ...)
1✔
191
        #'(lambda e ...))
×
192

193
    ;; core form to express deforestable operations
194
    (#%deforestable name:id info:id e:deforestable-clause ...)
1✔
195

196
    ;; backwards compat macro extensibility via Racket macros
197
    (~> ((~var ext-form (starts-with "qi:")) expr ...)
1✔
198
        #'(esc (ext-form expr ...)))
1✔
199
    ;; a literal is interpreted as a flow generating it
200
    (~> val:literal
1✔
201
        #'(gen val))
1✔
202
    ;; Certain rules of the language aren't determined by the "head"
203
    ;; position, so naively, these can't be core forms. In order to
204
    ;; treat them as core forms, we tag them at the expander level
205
    ;; by wrapping them with #%-prefixed forms, similar to Racket's
206
    ;; approach to a similiar case - "interposition points." These
207
    ;; new forms can then be treated as core forms in the compiler.
208
    ;;
209
    ;; Be careful with these tagging rules, though -- if they are too
210
    ;; lax in their match criteria they may produce infinite code
211
    ;; unless their output is matched prior to reaching the tagging rule.
212
    ;; So core forms expected to be produced by these tagging rules
213
    ;; should generally occur before the tagging rule
214
    (#%blanket-template (arg:arg-stx ...))
1✔
215
    (~> f:blanket-template-form
1✔
216
        #'(#%blanket-template f))
1✔
217

218
    (#%fine-template (arg:arg-stx ...))
1✔
219
    (~> f:fine-template-form
1✔
220
        #'(#%fine-template f))
1✔
221

222
    ;; When there is a partial application where a template hasn't
223
    ;; explicitly been indicated, we rewrite it to an equivalent use
224
    ;; of a blanket template.
225
    ;; We use a blanket rather than fine template since in such cases,
226
    ;; we cannot always infer the appropriate arity for a template
227
    ;; (e.g. it may change under composition within the form), while a
228
    ;; blanket template will accept any number of arguments
229
    (~> f:partial-application-form
1✔
230
        #:do [(define chirality (syntax-property this-syntax 'chirality))]
1✔
231
        (if (and chirality (eq? chirality 'right))
1✔
232
            (datum->syntax this-syntax
1✔
233
              (append (syntax->list this-syntax)
1✔
234
                      (list '__)))
1✔
235
            (datum->syntax this-syntax
1✔
236
              (let ([stx-list (syntax->list this-syntax)])
1✔
237
                (cons (car stx-list)
1✔
238
                      (cons '__ (cdr stx-list)))))))
1✔
239
    ;; literally indicated function identifier
240
    ;;
241
    ;; functions defined in the Qi binding space take precedence over
242
    ;; Racket definitions here, for cases of "library functions" like
243
    ;; `count` that we don't include in the core language but which
244
    ;; we'd like to treat as part of the language rather than as
245
    ;; functions which could be shadowed.
246
    (~> f:id
1✔
247
        #:with spaced-f (introduce-qi-syntax #'f)
1✔
248
        #'(esc spaced-f)))
1✔
249

250
  (nonterminal deforestable-clause
1✔
251
    ((~datum floe) e:closed-floe)
1✔
252
    ((~datum expr) g:racket-expr))
1✔
253

254
  (nonterminal arg-stx
1✔
255
    (~datum _)
1✔
256
    (~datum __)
1✔
257
    k:keyword
1✔
258

259
    e:racket-expr))
1✔
260

261

262
(module+ invoke
263
  (provide (for-syntax expand-flow))
264

265
  (begin-for-syntax
266
    (define (expand-flow stx)
267
      ((nonterminal-expander closed-floe) stx))))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc