matcher.scm 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #| -*-Scheme-*-
  2. Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
  3. 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
  4. 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Massachusetts
  5. Institute of Technology
  6. This file is part of MIT/GNU Scheme.
  7. MIT/GNU Scheme is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or (at
  10. your option) any later version.
  11. MIT/GNU Scheme is distributed in the hope that it will be useful, but
  12. WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with MIT/GNU Scheme; if not, write to the Free Software
  17. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
  18. USA.
  19. |#
  20. ;;;; Matcher based on match combinators, CPH/GJS style.
  21. ;;; Idea is in Hewitt's PhD thesis (1969).
  22. (declare (usual-integrations))
  23. ;;; There are match procedures that can be applied to data items. A
  24. ;;; match procedure either accepts or rejects the data it is applied
  25. ;;; to. Match procedures can be combined to apply to compound data
  26. ;;; items.
  27. ;;; A match procedure takes a list containing a data item, a
  28. ;;; dictionary, and a success continuation. The dictionary
  29. ;;; accumulates the assignments of match variables to values found in
  30. ;;; the data. The success continuation takes two arguments: the new
  31. ;;; dictionary, and the tail of the list resulting from matching its
  32. ;;; initial segment. If a match procedure fails it returns #f.
  33. ;;; Primitive match procedures:
  34. (define (match:predicate p?)
  35. (define (predicate-match data dictionary succeed)
  36. (and (pair? data)
  37. (p? (car data))
  38. (succeed dictionary (cdr data))))
  39. predicate-match)
  40. (define* (match:equal pattern-object #:optional equality?)
  41. (if (default-object? equality?) (set! equality? equal?))
  42. (match:predicate
  43. (lambda (data-object)
  44. (equality? pattern-object data-object))))
  45. ;;; A useful special case
  46. (define (match:eqv pattern-object)
  47. (define (eqv-match data dictionary succeed)
  48. (and (pair? data)
  49. (eqv? (car data) pattern-object)
  50. (succeed dictionary (cdr data))))
  51. eqv-match)
  52. (define* (match:element variable #:optional restriction?)
  53. ;;; FBE: move after difine
  54. ;;(if (default-object? restriction?) (set! restriction? (lambda (x) #t)))
  55. (define (element-match data dictionary succeed)
  56. (and (pair? data)
  57. (restriction? (car data))
  58. (let ((vcell (match:lookup variable dictionary)))
  59. (if vcell
  60. (and (datum=? (match:value vcell) (car data))
  61. (succeed dictionary (cdr data)))
  62. (succeed (match:bind variable (car data) dictionary)
  63. (cdr data))))))
  64. (if (default-object? restriction?) (set! restriction? (lambda (x) #t)))
  65. element-match)
  66. (define (match:segment variable)
  67. (define (segment-match data dictionary succeed)
  68. (and (or (pair? data) (null? data))
  69. (let ((vcell (match:lookup variable dictionary)))
  70. (if vcell
  71. (let ((v (match:value vcell)))
  72. (let ((end (match:segment-end v)))
  73. (let scan ((vptr (match:segment-beginning v))
  74. (dptr data))
  75. (cond ((eq? vptr end)
  76. (succeed dictionary dptr))
  77. ((not (pair? dptr)) #f)
  78. ((datum=? (car vptr) (car dptr))
  79. (scan (cdr vptr) (cdr dptr)))
  80. (else #f)))))
  81. (let try-seg ((end data))
  82. (or (succeed (match:bind variable
  83. (match:make-segment data end)
  84. dictionary)
  85. end)
  86. (and (pair? end)
  87. (try-seg (cdr end)))))))))
  88. segment-match)
  89. (define (match:make-segment begin end)
  90. (vector begin end))
  91. (define (match:segment-beginning value)
  92. (vector-ref value 0))
  93. (define (match:segment-end value)
  94. (vector-ref value 1))
  95. (define (match:list . match-combinators)
  96. (define (list-match data dictionary succeed)
  97. (and (pair? data)
  98. (let lp ((items (car data))
  99. (matchers match-combinators)
  100. (dictionary dictionary))
  101. (cond ((pair? matchers)
  102. ((car matchers) items dictionary
  103. (lambda (new-dictionary rest)
  104. (lp rest
  105. (cdr matchers)
  106. new-dictionary))))
  107. ((pair? items) #f)
  108. ((null? items)
  109. (succeed dictionary (cdr data)))
  110. (else #f)))))
  111. list-match)
  112. (define* (match:reverse-segment variable #:optional submatch)
  113. ;;; FBE: move after define
  114. ;;(if (default-object? submatch) (set! submatch match:equal))
  115. (define (reverse-segment-match data dictionary succeed)
  116. (if (list? data)
  117. (let ((vcell (match:lookup variable dictionary)))
  118. (if vcell
  119. (let ((v (match:value vcell)))
  120. (let ((beg (match:segment-beginning v))
  121. (end (match:segment-end v)))
  122. (let ((revseg
  123. (let revlp ((p beg) (rev '()))
  124. (cond ((eq? p end) rev)
  125. ((pair? p)
  126. (revlp (cdr p) (cons (car p) rev)))
  127. (else (error "Bad segment--reverse"))))))
  128. (let scan ((vptr revseg) (dptr data))
  129. (cond ((null? vptr)
  130. (succeed dictionary
  131. (list-tail data (length revseg))))
  132. ((not (pair? dptr)) #f)
  133. ((datum=? (car vptr) (car dptr))
  134. (scan (cdr vptr) (cdr dptr)))
  135. (else #f))))))
  136. #f))
  137. #f))
  138. (if (default-object? submatch) (set! submatch match:equal))
  139. reverse-segment-match)
  140. (define (datum=? datum1 datum2)
  141. (if (pair? datum1)
  142. (and (pair? datum2)
  143. (datum=? (car datum1) (car datum2))
  144. (datum=? (cdr datum1) (cdr datum2)))
  145. (eqv? datum1 datum2)))
  146. ;;; Support for the dictionary.
  147. (define (match:bind variable data-object dictionary)
  148. (cons (cons variable data-object) dictionary))
  149. (define (match:lookup variable dictionary)
  150. (assq variable dictionary))
  151. (define (match:value vcell)
  152. (cdr vcell))
  153. ;;; Syntax of matching is determined here.
  154. (define (match:->combinators pattern)
  155. (define (compile pattern)
  156. (cond ((match:element? pattern)
  157. (if (match:restricted? pattern)
  158. (match:element (match:variable-name pattern)
  159. (match:restriction pattern))
  160. (match:element (match:variable-name pattern))))
  161. ((match:segment? pattern)
  162. (match:segment (match:variable-name pattern)))
  163. ((match:reverse-segment? pattern)
  164. (match:reverse-segment (match:variable-name pattern)))
  165. ((null? pattern) (match:eqv '()))
  166. ((list? pattern)
  167. (apply match:list (map compile pattern)))
  168. (else (match:eqv pattern))))
  169. (compile pattern))
  170. #|
  171. ;;; In rule-syntax.scm
  172. (define (match:element? pattern)
  173. (and (pair? pattern)
  174. (eq? (car pattern) '?)))
  175. (define (match:segment? pattern)
  176. (and (pair? pattern)
  177. (eq? (car pattern) '??)))
  178. (define (match:variable-name pattern)
  179. (cadr pattern))
  180. (define (match:restricted? pattern)
  181. (not (null? (cddr pattern))))
  182. (define (match:restriction pattern)
  183. (caddr pattern))
  184. (define (match:reverse-segment? pattern)
  185. (and (pair? pattern)
  186. (eq? (car pattern) '$$)))
  187. |#
  188. #|
  189. ((match:->combinators '(a ((? b) 2 3) 1 c))
  190. '((a (1 2 3) 1 c))
  191. '()
  192. (lambda (x y) `(succeed ,x ,y)))
  193. ;Value: (succeed ((b . 1)) ())
  194. ((match:->combinators `(a ((? b ,number?) 2 3) 1 c))
  195. '((a (1 2 3) 1 c))
  196. '()
  197. (lambda (x y) `(succeed ,x ,y)))
  198. ;Value: (succeed ((b . 1)) ())
  199. ((match:->combinators `(a ((? b ,symbol?) 2 3) 1 c))
  200. '((a (1 2 3) 1 c))
  201. '()
  202. (lambda (x y) `(succeed ,x ,y)))
  203. ;Value: #f
  204. ((match:->combinators '(a ((? b) 2 3) (? b) c))
  205. '((a (1 2 3) 2 c))
  206. '()
  207. (lambda (x y) `(succeed ,x ,y)))
  208. ;Value: #f
  209. ((match:->combinators '(a ((? b) 2 3) (? b) c))
  210. '((a (1 2 3) 1 c))
  211. '()
  212. (lambda (x y) `(succeed ,x ,y)))
  213. ;Value: (succeed ((b . 1)) ())
  214. ((match:->combinators '(a (?? x) (?? y) (?? x) c))
  215. '((a b b b b b b c))
  216. '()
  217. (lambda (x y)
  218. (pp `(succeed ,x ,y))
  219. #f))
  220. (succeed ((y . #((b b b b b b c) (c))) (x . #((b b b b b b c) (b b b b b b c)))) ())
  221. (succeed ((y . #((b b b b b c) (b c))) (x . #((b b b b b b c) (b b b b b c)))) ())
  222. (succeed ((y . #((b b b b c) (b b c))) (x . #((b b b b b b c) (b b b b c)))) ())
  223. (succeed ((y . #((b b b c) (b b b c))) (x . #((b b b b b b c) (b b b c)))) ())
  224. ;Value: #f
  225. (define (palindrome? x)
  226. ((match:->combinators '((?? x) ($$ x)))
  227. (list x) '() (lambda (x y) (null? y))))
  228. ;Value: palindrome?
  229. (palindrome? '(a b c c b a))
  230. ;Value: #t
  231. (palindrome? '(a b c c a b))
  232. ;Value: #f
  233. |#