|
| 1 | +#lang racket/base |
| 2 | + |
| 3 | + |
| 4 | +(provide matching-comparator) |
| 5 | + |
| 6 | + |
| 7 | +(require (for-syntax racket/base |
| 8 | + racket/list |
| 9 | + racket/match |
| 10 | + racket/syntax) |
| 11 | + racket/match |
| 12 | + rebellion/base/comparator |
| 13 | + syntax/parse/define) |
| 14 | + |
| 15 | + |
| 16 | +;@---------------------------------------------------------------------------------------------------- |
| 17 | + |
| 18 | + |
| 19 | +(begin-for-syntax |
| 20 | + (define-splicing-syntax-class compare-directive |
| 21 | + #:attributes (comparable-expr comparator-expr comparator-expr-lexical-context) |
| 22 | + |
| 23 | + (pattern (~seq #:compare comparable-expr:expr comparator-expr:expr) |
| 24 | + #:with comparator-expr-lexical-context (syntax-local-introduce (attribute comparator-expr))) |
| 25 | + |
| 26 | + (pattern (~seq #:compare comparable-expr:expr) |
| 27 | + #:with comparator-expr #'real<=> |
| 28 | + #:with comparator-expr-lexical-context (attribute comparator-expr))) |
| 29 | + |
| 30 | + (define-syntax-class matching-comparator-clause |
| 31 | + #:attributes (match-pattern |
| 32 | + [comparable-expr 1] |
| 33 | + [comparator-expr 1] |
| 34 | + [comparator-expr-lexical-context 1]) |
| 35 | + (pattern [match-pattern :compare-directive ...]))) |
| 36 | + |
| 37 | + |
| 38 | +(define (return-comparison-unless-equivalent comparison) |
| 39 | + (and (not (equal? comparison equivalent)) comparison)) |
| 40 | + |
| 41 | + |
| 42 | +(begin-for-syntax |
| 43 | + (define (build-static-comparison-chain comparisons) |
| 44 | + (define/with-syntax (comparison ... last-comparison) comparisons) |
| 45 | + #'(or (return-comparison-unless-equivalent comparison) ... last-comparison))) |
| 46 | + |
| 47 | + |
| 48 | +(define-syntax-parse-rule (matching-comparator clause:matching-comparator-clause ...+) |
| 49 | + |
| 50 | + #:with ((comparator-id ...) ...) |
| 51 | + (let ([counts (make-hash '())]) |
| 52 | + (for/list ([pattern-comparator-exprs (in-list (attribute clause.comparator-expr))] |
| 53 | + [pattern-comparator-contexts |
| 54 | + (in-list (attribute clause.comparator-expr-lexical-context))] |
| 55 | + [pattern-index (in-naturals)]) |
| 56 | + (for/list ([expr (in-list pattern-comparator-exprs)] |
| 57 | + [context (in-list pattern-comparator-contexts)] |
| 58 | + [i (in-naturals)]) |
| 59 | + (cond |
| 60 | + [(identifier? expr) |
| 61 | + (define occurrence-index (hash-ref! counts (syntax-e expr) 0)) |
| 62 | + (hash-update! counts (syntax-e expr) add1) |
| 63 | + (format-id context "~a-id~a" expr occurrence-index #:subs? #false)] |
| 64 | + [else (format-id context "pattern~a-comparator~a" pattern-index i)])))) |
| 65 | + |
| 66 | + #:with (([left-comparable-thunk-id right-comparable-thunk-id] ...) ...) |
| 67 | + (for/list ([pattern-comparable-exprs (in-list (attribute clause.comparable-expr))] |
| 68 | + [pattern-index (in-naturals)]) |
| 69 | + (define counts (make-hash)) |
| 70 | + (for/list ([expr (in-list pattern-comparable-exprs)] |
| 71 | + [i (in-naturals)]) |
| 72 | + (define (make-id side) |
| 73 | + (define context (syntax-local-introduce expr)) |
| 74 | + (cond |
| 75 | + [(identifier? expr) |
| 76 | + (define occurrence-index (hash-ref! counts (syntax-e expr) 0)) |
| 77 | + (hash-update! counts (syntax-e expr) add1) |
| 78 | + (format-id context "~a-~a~a" side expr (if (zero? occurrence-index) "" occurrence-index) |
| 79 | + #:subs? #false)] |
| 80 | + [else |
| 81 | + (format-id context "~a-pattern~a-comparable~a" side pattern-index i)])) |
| 82 | + (list (make-id 'left) (make-id 'right)))) |
| 83 | + |
| 84 | + #:with (pattern-index ...) |
| 85 | + (for/list ([i (in-range 0 (length (attribute clause.match-pattern)))]) |
| 86 | + i) |
| 87 | + |
| 88 | + #:with (comparable-values-expr ...) |
| 89 | + (for/list ([pattern-comaprable-exprs (in-list (attribute clause.comparable-expr))] |
| 90 | + [pattern-index (in-naturals)]) |
| 91 | + (build-comparable-values-expression pattern-index pattern-comaprable-exprs)) |
| 92 | + |
| 93 | + #:with left-comparables-id #'left-comparables |
| 94 | + #:with right-comparables-id #'right-comparables |
| 95 | + |
| 96 | + #:with (comparable-values-comparison ...) |
| 97 | + (for/list ([pattern-comparator-ids (in-list (attribute comparator-id))] |
| 98 | + [pattern-comparable-exprs (in-list (attribute clause.comparable-expr))] |
| 99 | + [pattern-left-comparable-ids (in-list (attribute left-comparable-thunk-id))] |
| 100 | + [pattern-right-comparable-ids (in-list (attribute right-comparable-thunk-id))]) |
| 101 | + (build-comparable-values-comparison (attribute left-comparables-id) |
| 102 | + (attribute right-comparables-id) |
| 103 | + pattern-comparator-ids |
| 104 | + pattern-comparable-exprs |
| 105 | + pattern-left-comparable-ids |
| 106 | + pattern-right-comparable-ids)) |
| 107 | + |
| 108 | + (let ([comparator-id clause.comparator-expr] ... ...) |
| 109 | + (define (pattern-selector v) |
| 110 | + (match v |
| 111 | + [clause.match-pattern comparable-values-expr] |
| 112 | + ...)) |
| 113 | + (make-comparator |
| 114 | + (λ (left right) |
| 115 | + (define-values (left-index left-comparables-id) (pattern-selector left)) |
| 116 | + (define-values (right-index right-comparables-id) (pattern-selector right)) |
| 117 | + (cond |
| 118 | + [(< left-index right-index) lesser] |
| 119 | + [(> left-index right-index) greater] |
| 120 | + [(equal? left-index 'pattern-index) comparable-values-comparison] |
| 121 | + ...))))) |
| 122 | + |
| 123 | + |
| 124 | +(begin-for-syntax |
| 125 | + (define (build-comparable-values-expression index comparable-exprs) |
| 126 | + (define wrapped-comparable-exprs |
| 127 | + (for/list ([e (in-list comparable-exprs)]) |
| 128 | + (if (identifier? e) e #`(λ () #,e)))) |
| 129 | + (define wrapped-comparables |
| 130 | + (match (length comparable-exprs) |
| 131 | + [0 #'#false] |
| 132 | + [1 (first wrapped-comparable-exprs)] |
| 133 | + [_ #`(λ () (values #,@wrapped-comparable-exprs))])) |
| 134 | + #`(values '#,index #,wrapped-comparables))) |
| 135 | + |
| 136 | + |
| 137 | +(begin-for-syntax |
| 138 | + (define (build-comparable-values-comparison left-values-id |
| 139 | + right-values-id |
| 140 | + comparator-ids |
| 141 | + comparable-exprs |
| 142 | + left-comparable-ids |
| 143 | + right-comparable-ids) |
| 144 | + (define comparisons |
| 145 | + (for/list ([e (in-list comparable-exprs)] |
| 146 | + [comparator-id (in-list comparator-ids)] |
| 147 | + [left (in-list left-comparable-ids)] |
| 148 | + [right (in-list right-comparable-ids)]) |
| 149 | + (if (identifier? e) |
| 150 | + #`(compare #,comparator-id #,left #,right) |
| 151 | + #`(compare #,comparator-id (#,left) (#,right))))) |
| 152 | + (match (length comparator-ids) |
| 153 | + [0 #'equivalent] |
| 154 | + [1 #`(compare #,(first comparator-ids) #,left-values-id #,right-values-id)] |
| 155 | + [_ |
| 156 | + #`(let-values ([(#,@left-comparable-ids) (#,left-values-id)] |
| 157 | + [(#,@right-comparable-ids) (#,right-values-id)]) |
| 158 | + #,(build-static-comparison-chain comparisons))]))) |
0 commit comments