no-order.scrbl (8339B)
1 #lang scribble/manual 2 @require[scribble/example 3 "utils.rkt" 4 @for-label[phc-toolkit/untyped 5 extensible-parser-specifications 6 generic-syntax-expanders 7 racket/base 8 syntax/parse 9 (only-in racket/base [... …])]] 10 11 @title{Matching alternatives in any order} 12 13 @defform[#:kind "pattern expander" 14 #:literals (~mixin ~or) 15 (~seq-no-order clause-or-mixin ...) 16 #:grammar 17 [(clause-or-mixin #,ntax-pattern 18 (~mixin #,-alternative-mixin) 19 (~or clause-or-mixin ...) 20 derived-or)]]{ 21 Splicing pattern which matches the given @racket[clause-or-mixin]s in any 22 order, enforcing the global constraints expressed within each. 23 24 Nested @racket[~or] directly below @racket[~seq-no-order] are recursively 25 inlined. In other words, the @racket[~or] present directly below the 26 @racket[~seq-no-order] or below such an @racket[~or] clause do not behave as 27 "exclusive or", but instead contain clauses which can appear in any order. 28 These clauses are not grouped in any way by the @racket[~or], i.e. 29 @racket[(~no-order (~or (~or a b) (~or c d)))] is equivalent to 30 @racket[(~no-order a b c d)]. 31 32 The @racket[derived-or] term covers any 33 @tech[#:doc '(lib "syntax/scribblings/syntax.scrbl")]{pattern expander} or 34 @tech{eh-mixin expander} application which expands to a 35 @racket[clause-or-mixin]. The expansion of pattern and eh-mixin expanders 36 happens before inlining the top @racket[~or] clauses.} 37 38 @defform[#:kind "pattern expander" 39 #:literals (~mixin ~or) 40 (~no-order clause-or-mixin ...) 41 #:grammar 42 [(clause-or-mixin #,ntax-pattern 43 (~mixin #,-alternative-mixin) 44 (~or clause-or-mixin ...) 45 derived-or)]]{ 46 47 Like @racket[~seq-no-order], except that it matches a syntax list, instead of 48 being spliced into the surrounding sequence of patterns. In other words, 49 50 @racketblock[({~seq-no-order clause-or-mixin ...})] 51 52 is equivalent to (notice the extra pair of braces above): 53 54 @racketblock[(~no-order clause-or-mixin ...)] 55 56 Additionally, @racket[~no-order] can include clauses which use 57 @racket[~lift-rest], which lifts a pattern which matches the tail of an 58 improper list.} 59 60 @section{Enforcing a partial order on the alternatives} 61 62 @defform[#:kind "eh-mixin expander" 63 (~order-point point-name #,ntax-pattern ...)]{ 64 When parsing a sequence of elements, @racket[~seq-no-order] and 65 @racket[~no-order] associate an increasing number to each element starting from 66 zero. 67 68 The number associated with the first element matched by 69 @racket[#,ntax-pattern ...] is memorised into the attribute 70 @racket[point-name]. 71 72 This allows the position of elements matched by otherwise independent mixins to 73 be compared using @racket[order-point<] and @racket[order-point>]} 74 75 @defform[(order-point< a b) 76 #:grammar 77 [(a #,tribute-name) 78 (b #,tribute-name)]]{ 79 Returns @racket[#t] when the first element matched by 80 @racket[(~order-point a #,ntax-pattern ...)] occurs before the first element 81 matched by @racket[(~order-point b #,ntax-pattern ...)]. Otherwise, returns 82 @racket[#f]. 83 84 This operation does not fail if @racket[a] or @racket[b] are bound to 85 @racket[#f] (i.e. their corresponding @racket[_syntax-pattern ...] did not 86 match). Instead, in both cases, it returns @racket[#f].} 87 88 @defform[(order-point> a b) 89 #:grammar 90 [(a #,tribute-name) 91 (b #,tribute-name)]]{ 92 Returns @racket[#t] when the first element matched by 93 @racket[(~order-point a #,ntax-pattern ...)] occurs after the first element 94 matched by @racket[(~order-point b #,ntax-pattern ...)]. Otherwise, returns 95 @racket[#f]. 96 97 This operation does not fail if @racket[a] or @racket[b] are bound to 98 @racket[#f] (i.e. their corresponding @racket[_syntax-pattern ...] did not 99 match). Instead, in both cases, it returns @racket[#f].} 100 101 @defform[(try-order-point< a b) 102 #:grammar 103 [(a #,tribute-name) 104 (b #,tribute-name)]]{ 105 106 Like @racket[order-point<], except that it does not fail if @racket[a] or 107 @racket[b] are not attributes, or if they are bound to @racket[#f]. Instead, in 108 all those cases, it returns @racket[#f]. 109 110 It can be used as follows: 111 112 @racketblock[ 113 (~post-fail "a must appear after b" 114 #:when (try-order-point< a b))] 115 116 The same caveats as for @racket[try-attribute] apply.} 117 118 @defform[(try-order-point> a b) 119 #:grammar 120 [(a #,tribute-name) 121 (b #,tribute-name)]]{ 122 123 Like @racket[order-point>], except that it does not fail if @racket[a] or 124 @racket[b] are not attributes, or if they are bound to @racket[#f]. Instead, in 125 all those cases, it returns @racket[#f]. 126 127 It can be used as follows: 128 129 @racketblock[ 130 (~post-fail "a must appear before b" 131 #:when (try-order-point> a b))] 132 133 The same caveats as for @racket[try-attribute] apply.} 134 135 @defform[#:kind "eh-mixin-expander" 136 (~before other message pat ...)]{ 137 138 Post-checks that the first element matched by @racket[pat ...] appears before 139 the @racket[other] order-point. This is a shorthand for: 140 141 @racketblock[{~order-point pt 142 {~seq pat ...} 143 {~post-fail message #:when (order-point> pt other)}}] 144 145 Note: Hopefully @racket[~before] will be modified in the future so that it 146 auto-detects if the @racket[other] order-point is not defined as part of the 147 current @racket[~no-order]. Do not rely on comparisons with order points 148 somehow defined outside the current @racket[~no-order], as that behaviour may 149 change in the future. 150 151 This is implemented as a @seclink["Pre__global_and_post_operations"]{pre 152 operation}.} 153 154 @defform[#:kind "eh-mixin-expander" 155 (~after other message pat ...)]{ 156 Post-checks that the first element matched by @racket[pat ...] appears after 157 the @racket[other] order-point. This is a shorthand for: 158 159 @racketblock[{~order-point pt 160 {~seq pat ...} 161 {~post-fail message #:when (order-point< pt other)}}] 162 163 Note: Hopefully @racket[~after] will be modified in the future so that it 164 auto-detects if the @racket[other] order-point is not defined as part of the 165 current @racket[~no-order]. Do not rely on comparisons with order points 166 somehow defined outside the current @racket[~no-order], as that behaviour may 167 change in the future. 168 169 This is implemented as a @seclink["Pre__global_and_post_operations"]{pre 170 operation}.} 171 172 @defform[#:kind "eh-mixin-expander" 173 (~try-before other message pat ...)]{ 174 175 Post-checks that the first element matched by @racket[pat ...] appears before 176 the @racket[other] order-point. The @racket[try-] version does not cause an 177 error if the order-point @racket[other] is not define (e.g. it was part of 178 another mixin which was not included). This is a shorthand for: 179 180 @racketblock[{~order-point pt 181 {~seq pat ...} 182 {~post-fail message #:when (try-order-point> pt other)}}] 183 184 Note: Hopefully @racket[~before] will be modified in the future so that it 185 auto-detects if the @racket[other] order-point is missing. This form will then 186 be removed. 187 188 This is implemented as a @seclink["Pre__global_and_post_operations"]{pre 189 operation}.} 190 191 @defform[#:kind "eh-mixin-expander" 192 (~try-after other message pat ...)]{ 193 Post-checks that the first element matched by @racket[pat ...] appears after 194 the @racket[other] order-point. The @racket[try-] version does not cause an 195 error if the order-point @racket[other] is not define (e.g. it was part of 196 another mixin which was not included). This is a shorthand for: 197 198 @racketblock[{~order-point pt 199 {~seq pat ...} 200 {~post-fail message #:when (try-order-point< pt other)}}] 201 202 Note: Hopefully @racket[~after] will be modified in the future so that it 203 auto-detects if the @racket[other] order-point is missing. This form will then 204 be removed. 205 206 This is implemented as a @seclink["Pre__global_and_post_operations"]{pre 207 operation}.} 208