added match-case just in, uh, case
Getty Ritter
10 years ago
| 1 | (declare (unit match-case-simple)) | |
| 2 | ; A simple linear pattern matcher | |
| 3 | ; It is efficient (generates code at macro-expansion time) and simple: | |
| 4 | ; it should work on any R5RS Scheme system. | |
| 5 | ; | |
| 6 | ; It was first developed for the leanTAP theorem prover in miniKanren. | |
| 7 | ; It has been in the miniKanren repository | |
| 8 | ; http://kanren.sf.net/viewvc/kanren/kanren/mini/leanTAP.scm?view=log | |
| 9 | ; since August 2005. | |
| 10 | ; | |
| 11 | ; See the above code for the example of using match-case-simple: | |
| 12 | ; transforming a first-order logic formula to the Negation Normal Form. | |
| 13 | ||
| 14 | ||
| 15 | ; (match-case-simple exp <clause> ...[<else-clause>]) | |
| 16 | ; <clause> ::= (<pattern> <guard> exp ...) | |
| 17 | ; <else-clause> ::= (else exp ...) | |
| 18 | ; <guard> ::= boolean exp | () | |
| 19 | ; <pattern> :: = | |
| 20 | ; ,var -- matches always and binds the var | |
| 21 | ; pattern must be linear! No check is done | |
| 22 | ; __ -- matches always | |
| 23 | ; 'exp -- comparison with exp (using equal?) | |
| 24 | ; exp -- comparison with exp (using equal?) | |
| 25 | ; (<pattern1> <pattern2> ...) -- matches the list of patterns | |
| 26 | ; (<pattern1> . <pattern2>) -- ditto | |
| 27 | ; () -- matches the empty list | |
| 28 | ||
| 29 | ; In the original version, the always-matching pattern was specified | |
| 30 | ; as a simple underscore. That does not work in R6RS which reserves | |
| 31 | ; the underscore. Therefore, the always-matching pattern is changed | |
| 32 | ; to two underscores. | |
| 33 | ||
| 34 | (define-syntax match-case-simple | |
| 35 | (syntax-rules () | |
| 36 | ((_ exp clause ...) | |
| 37 | (let ((val-to-match exp)) | |
| 38 | (match-case-simple* val-to-match clause ...))))) | |
| 39 | ||
| 40 | (define (match-failure val) | |
| 41 | (error "failed match" val)) | |
| 42 | ||
| 43 | (define-syntax match-case-simple* | |
| 44 | (syntax-rules (else) | |
| 45 | ((_ val (else exp ...)) | |
| 46 | (let () exp ...)) | |
| 47 | ((_ val) | |
| 48 | (match-failure val)) | |
| 49 | ((_ val (pattern () exp ...) . clauses) | |
| 50 | (let ((fail (lambda () (match-case-simple* val . clauses)))) | |
| 51 | ; note that match-pattern may do binding. Here, | |
| 52 | ; other clauses are outside of these binding | |
| 53 | (match-pattern val pattern (let () exp ...) (fail)))) | |
| 54 | ((_ val (pattern guard exp ...) . clauses) | |
| 55 | (let ((fail (lambda () (match-case-simple* val . clauses)))) | |
| 56 | (match-pattern val pattern | |
| 57 | (if guard (let () exp ...) (fail)) | |
| 58 | (fail)))) | |
| 59 | )) | |
| 60 | ||
| 61 | ||
| 62 | ; (match-pattern val pattern kt kf) | |
| 63 | (define-syntax match-pattern | |
| 64 | (syntax-rules (__ quote unquote) | |
| 65 | ((_ val __ kt kf) kt) | |
| 66 | ((_ val () kt kf) | |
| 67 | (if (null? val) kt kf)) | |
| 68 | ((_ val (quote lit) kt kf) | |
| 69 | (if (equal? val (quote lit)) kt kf)) | |
| 70 | ((_ val (unquote var) kt kf) | |
| 71 | (let ((var val)) kt)) | |
| 72 | ((_ val (x . y) kt kf) | |
| 73 | (if (pair? val) | |
| 74 | (let ((valx (car val)) | |
| 75 | (valy (cdr val))) | |
| 76 | (match-pattern valx x | |
| 77 | (match-pattern valy y kt kf) | |
| 78 | kf)) | |
| 79 | kf)) | |
| 80 | ((_ val lit kt kf) | |
| 81 | (if (equal? val (quote lit)) kt kf)))) | |
| 82 | ||
| 83 | number? | |
| 84 | ||
| 85 | ; A simple example: | |
| 86 | '(let () | |
| 87 | (define (test-match x) | |
| 88 | (match-case-simple x | |
| 89 | (,x (number? x) "number") | |
| 90 | (() () "nil") | |
| 91 | (#t () "bool") | |
| 92 | (#f () "bool") | |
| 93 | ((,x . ,y) () | |
| 94 | (string-append "pair of " (test-match x) " and " (test-match y))) | |
| 95 | (__ () "something else"))) | |
| 96 | (for-each (lambda (x) (display (test-match x)) (newline)) | |
| 97 | '(1 #t "str" (1 2 3)))) | |
| 98 | ;; printed result: | |
| 99 | ||
| 100 | ;; number | |
| 101 | ;; bool | |
| 102 | ;; something else | |
| 103 | ;; pair of number and pair of number and pair of number and nil | |
| 104 | ||
| 105 | ||
| 106 | ; more complex example: meta-circular interpreter | |
| 107 | ||
| 108 | '(let () | |
| 109 | (define (int code env) | |
| 110 | (match-case-simple code | |
| 111 | (('quote ,x) () x) | |
| 112 | ((let ((,x ,e)) ,body) (symbol? x) | |
| 113 | (let ((xv (int e env))) | |
| 114 | (int body (cons (cons x xv) env)))) | |
| 115 | ((lambda () ,body) () ; thunk | |
| 116 | (lambda () (int body env))) ; closed over the env | |
| 117 | ((lambda ,argl ,body) (symbol? argl) ; arglist | |
| 118 | (lambda arglv | |
| 119 | (int body (cons (cons argl arglv) env)))) | |
| 120 | ((lambda (,x) ,body) (symbol? x) ; 1-arg function | |
| 121 | (lambda (xv) | |
| 122 | (int body (cons (cons x xv) env)))) | |
| 123 | ; the general case of lambda is skipped to keep the example small | |
| 124 | ((,op . ,args) () | |
| 125 | (let* ((opv (int op env)) | |
| 126 | (argvs (map (lambda (c) (int c env)) args))) | |
| 127 | (apply opv argvs))) | |
| 128 | (,x (symbol? x) (lookup x env)) | |
| 129 | (,x () x) ; probably number, string, etc. | |
| 130 | )) | |
| 131 | ||
| 132 | ; Lookup a symbol in the environment | |
| 133 | (define (lookup x env) | |
| 134 | (cond | |
| 135 | ((assq x env) => cdr) | |
| 136 | (else (error "Can't find " x)))) | |
| 137 | ||
| 138 | ; Initial environment | |
| 139 | (define env0 | |
| 140 | (map (lambda (x) (cons x (eval x (interaction-environment)))) | |
| 141 | '(+ - display))) ; add more | |
| 142 | ||
| 143 | ; tests | |
| 144 | ||
| 145 | (int 1 env0) ; 1 | |
| 146 | (int '1 env0) ; '1 is the same as 1 | |
| 147 | ||
| 148 | (int '(quote x) env0) ; x | |
| 149 | ||
| 150 | (int '(display 'x) env0) ; x | |
| 151 | ||
| 152 | (int '(display x) env0) ; error: unbound x | |
| 153 | ||
| 154 | (int '(let ((x (+ 1 2 3))) (display x)) env0) ; 6 | |
| 155 | ((int '(lambda () 1) env0)) ; 1 | |
| 156 | ((int '(lambda (x) x) env0) 1) ; 1 | |
| 157 | (((int '(lambda (x) (lambda (y) (+ x y))) env0) 2) 3) ; 5 (test closure) | |
| 158 | ||
| 159 | ((int '(lambda l (display l)) env0) 1 2 3) ; (1 2 3) | |
| 160 | ) | |
| 161 |