patmatch.lisp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. ;;;; -*- Mode: Lisp; Syntax: Common-Lisp -*-
  2. ;;;; Code from Paradigms of AI Programming
  3. ;;;; Copyright (c) 1991 Peter Norvig
  4. ;;;; File pat-match.lisp: Pattern matcher from section 6.2
  5. ;;; Two bug fixes By Richard Fateman, rjf@cs.berkeley.edu October 92.
  6. ;;; The basic are in auxfns.lisp; look for "PATTERN MATCHING FACILITY"
  7. (in-package :cl-user)
  8. (defpackage chatikbot.patmatch
  9. (:use :cl)
  10. (:export :?is
  11. :?or
  12. :?and
  13. :?not
  14. :?*
  15. :?+
  16. :??
  17. :?if
  18. :rule-based-translator))
  19. (in-package #:chatikbot.patmatch)
  20. (defconstant fail nil "Indicates pat-match failure")
  21. (defparameter no-bindings '((t . t))
  22. "Indicates pat-match success, with no variables.")
  23. (defun get-binding (var bindings)
  24. "Find a (variable . value) pair in a binding list."
  25. (assoc var bindings))
  26. (defun binding-val (binding)
  27. "Get the value part of a single binding."
  28. (cdr binding))
  29. (defun extend-bindings (var val bindings)
  30. "Add a (var . value) pair to a binding list."
  31. (cons (cons var val) bindings))
  32. (defun match-variable (var input bindings)
  33. "Does VAR match input? Uses (or updates) and returns bindings."
  34. (let ((binding (get-binding var bindings)))
  35. (cond ((not binding) (extend-bindings var input bindings))
  36. ((equal input (binding-val binding)) bindings)
  37. (t fail))))
  38. (defun variable-p (x)
  39. "Is x a variable (a symbol beginning with `?')?"
  40. (and (symbolp x) (equal (elt (symbol-name x) 0) #\?)))
  41. (defun pat-match (pattern input &optional (bindings no-bindings))
  42. "Match pattern against input in the context of the bindings"
  43. (cond ((eq bindings fail) fail)
  44. ((variable-p pattern)
  45. (match-variable pattern input bindings))
  46. ((eql pattern input) bindings)
  47. ((segment-pattern-p pattern)
  48. (segment-matcher pattern input bindings))
  49. ((single-pattern-p pattern) ; ***
  50. (single-matcher pattern input bindings)) ; ***
  51. ((and (consp pattern) (consp input))
  52. (pat-match (rest pattern) (rest input)
  53. (pat-match (first pattern) (first input)
  54. bindings)))
  55. (t fail)))
  56. (setf (get '?is 'single-match) 'match-is)
  57. (setf (get '?or 'single-match) 'match-or)
  58. (setf (get '?and 'single-match) 'match-and)
  59. (setf (get '?not 'single-match) 'match-not)
  60. (setf (get '?* 'segment-match) 'segment-match)
  61. (setf (get '?+ 'segment-match) 'segment-match+)
  62. (setf (get '?? 'segment-match) 'segment-match?)
  63. (setf (get '?if 'segment-match) 'match-if)
  64. (defun segment-pattern-p (pattern)
  65. "Is this a segment-matching pattern like ((?* var) . pat)?"
  66. (and (consp pattern) (consp (first pattern))
  67. (symbolp (first (first pattern)))
  68. (segment-match-fn (first (first pattern)))))
  69. (defun single-pattern-p (pattern)
  70. "Is this a single-matching pattern?
  71. E.g. (?is x predicate) (?and . patterns) (?or . patterns)."
  72. (and (consp pattern)
  73. (single-match-fn (first pattern))))
  74. (defun segment-matcher (pattern input bindings)
  75. "Call the right function for this kind of segment pattern."
  76. (funcall (segment-match-fn (first (first pattern)))
  77. pattern input bindings))
  78. (defun single-matcher (pattern input bindings)
  79. "Call the right function for this kind of single pattern."
  80. (funcall (single-match-fn (first pattern))
  81. (rest pattern) input bindings))
  82. (defun segment-match-fn (x)
  83. "Get the segment-match function for x,
  84. if it is a symbol that has one."
  85. (when (symbolp x) (get x 'segment-match)))
  86. (defun single-match-fn (x)
  87. "Get the single-match function for x,
  88. if it is a symbol that has one."
  89. (when (symbolp x) (get x 'single-match)))
  90. (defun match-is (var-and-pred input bindings)
  91. "Succeed and bind var if the input satisfies pred,
  92. where var-and-pred is the list (var pred)."
  93. (let* ((var (first var-and-pred))
  94. (pred (second var-and-pred))
  95. (new-bindings (pat-match var input bindings)))
  96. (if (or (eq new-bindings fail)
  97. (not (funcall pred input)))
  98. fail
  99. new-bindings)))
  100. (defun match-and (patterns input bindings)
  101. "Succeed if all the patterns match the input."
  102. (cond ((eq bindings fail) fail)
  103. ((null patterns) bindings)
  104. (t (match-and (rest patterns) input
  105. (pat-match (first patterns) input
  106. bindings)))))
  107. (defun match-or (patterns input bindings)
  108. "Succeed if any one of the patterns match the input."
  109. (if (null patterns)
  110. fail
  111. (let ((new-bindings (pat-match (first patterns)
  112. input bindings)))
  113. (if (eq new-bindings fail)
  114. (match-or (rest patterns) input bindings)
  115. new-bindings))))
  116. (defun match-not (patterns input bindings)
  117. "Succeed if none of the patterns match the input.
  118. This will never bind any variables."
  119. (if (match-or patterns input bindings)
  120. fail
  121. bindings))
  122. (defun segment-match (pattern input bindings &optional (start 0))
  123. "Match the segment pattern ((?* var) . pat) against input."
  124. (let ((var (second (first pattern)))
  125. (pat (rest pattern)))
  126. (if (null pat)
  127. (match-variable var input bindings)
  128. (let ((pos (first-match-pos (first pat) input start)))
  129. (if (null pos)
  130. fail
  131. (let ((b2 (pat-match
  132. pat (subseq input pos)
  133. (match-variable var (subseq input 0 pos)
  134. bindings))))
  135. ;; If this match failed, try another longer one
  136. (if (eq b2 fail)
  137. (segment-match pattern input bindings (+ pos 1))
  138. b2)))))))
  139. (defun first-match-pos (pat1 input start)
  140. "Find the first position that pat1 could possibly match input,
  141. starting at position start. If pat1 is non-constant, then just
  142. return start."
  143. (cond ((and (atom pat1) (not (variable-p pat1)))
  144. (position pat1 input :start start :test #'equal))
  145. ((<= start (length input)) start) ;*** fix, rjf 10/1/92 (was <)
  146. (t nil)))
  147. (defun segment-match+ (pattern input bindings)
  148. "Match one or more elements of input."
  149. (segment-match pattern input bindings 1))
  150. (defun segment-match? (pattern input bindings)
  151. "Match zero or one element of input."
  152. (let ((var (second (first pattern)))
  153. (pat (rest pattern)))
  154. (or (pat-match (cons var pat) input bindings)
  155. (pat-match pat input bindings))))
  156. (defun match-if (pattern input bindings)
  157. "Test an arbitrary expression involving variables.
  158. The pattern looks like ((?if code) . rest)."
  159. ;; *** fix, rjf 10/1/92 (used to eval binding values)
  160. (and (progv (mapcar #'car bindings)
  161. (mapcar #'cdr bindings)
  162. (eval (second (first pattern))))
  163. (pat-match (rest pattern) input bindings)))
  164. (defun pat-match-abbrev (symbol expansion)
  165. "Define symbol as a macro standing for a pat-match pattern."
  166. (setf (get symbol 'expand-pat-match-abbrev)
  167. (expand-pat-match-abbrev expansion)))
  168. (defun expand-pat-match-abbrev (pat)
  169. "Expand out all pattern matching abbreviations in pat."
  170. (cond ((and (symbolp pat) (get pat 'expand-pat-match-abbrev)))
  171. ((atom pat) pat)
  172. (t (cons (expand-pat-match-abbrev (first pat))
  173. (expand-pat-match-abbrev (rest pat))))))
  174. (defun rule-based-translator
  175. (input rules &key (matcher 'pat-match)
  176. (rule-if #'first) (rule-then #'rest) (action #'sublis))
  177. "Find the first rule in rules that matches input,
  178. and apply the action to that rule."
  179. (some
  180. #'(lambda (rule)
  181. (let ((result (funcall matcher (funcall rule-if rule)
  182. input)))
  183. (if (not (eq result fail))
  184. (funcall action result (funcall rule-then rule)))))
  185. rules))