Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/patmatch.lisp
| Kind | Covered | All | % |
| expression | 117 | 346 | 33.8 |
| branch | 30 | 70 | 42.9 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: org.datagraph.spocq.implementation; -*-
3
(in-package :org.datagraph.spocq.implementation)
5
(:documentation "This file defines the DISTINCT operator for the 'org.datagraph.spocq' RDF engine."
8
"Copyright (c) 1991 Peter Norvig;
9
Two bug fixes By Richard Fateman, rjf@cs.berkeley.edu October 92.
10
Copyright 2010 [james anderson](mailto:james.anderson@setf.de).")
13
"This is based on the 'File pat-match.lisp: Pattern matcher from section 6.2' from Norvig's
14
'Principles of AI Programming' under its [license](http://norvig.com/license.html), with
16
- integrate in the spocq implementation package
17
- rename conflicting operators
18
- add the operator, ?bind, which adds a bindings to the current environment
20
Several of the macro expansions for algebra operators speculate with the rule-based-translator
21
preliminary to a 'standard' expansion to recognize and translate idioms."))
24
(defconstant fail nil "Indicates pat-match failure")
26
(defvar no-bindings '((t . t))
27
"Indicates pat-match success, with no variables.")
30
(defun match-variable-p (x)
31
"Is x a variable (a symbol beginning with `?')?"
32
(and (symbolp x) (equal (char (symbol-name x) 0) #\?)))
34
(defun get-binding (var bindings)
35
"Find a (variable . value) pair in a binding list."
38
(defun binding-var (binding)
39
"Get the variable part of a single binding."
42
(defun binding-val (binding)
43
"Get the value part of a single binding."
46
(defun make-binding (var val) (cons var val))
48
(defun lookup (var bindings)
49
"Get the value part (for var) from a binding list."
50
(binding-val (get-binding var bindings)))
52
(defun extend-bindings (var val bindings)
53
"Add a (var . value) pair to a binding list."
54
(cons (make-binding var val)
55
;; Once we add a "real" binding,
56
;; we can get rid of the dummy no-bindings
57
(if (eq bindings no-bindings)
61
(defun match-variable (var input bindings)
62
"Does VAR match input? Uses (or updates) and returns bindings."
63
(let ((binding (get-binding var bindings)))
64
(cond ((not binding) (extend-bindings var input bindings))
65
((equal input (binding-val binding)) bindings)
68
(setf (get '?is 'single-match) 'match-is)
69
(setf (get '?or 'single-match) 'match-or)
70
(setf (get '?and 'single-match) 'match-and)
71
(setf (get '?not 'single-match) 'match-not)
73
(setf (get '?* 'segment-match) 'segment-match)
74
(setf (get '?+ 'segment-match) 'segment-match+)
75
(setf (get '?? 'segment-match) 'segment-match?)
76
(setf (get '?if 'segment-match) 'match-if)
78
(defun segment-pattern-p (pattern)
79
"Is this a segment-matching pattern like ((?* var) . pat)?"
80
(and (consp pattern) (consp (first pattern))
81
(symbolp (first (first pattern)))
82
(segment-match-fn (first (first pattern)))))
84
(defun single-pattern-p (pattern)
85
"Is this a single-matching pattern?
86
E.g. (?is x predicate) (?and . patterns) (?or . patterns)."
88
(single-match-fn (first pattern))))
90
(defun segment-matcher (pattern input bindings)
91
"Call the right function for this kind of segment pattern."
92
(funcall (segment-match-fn (first (first pattern)))
93
pattern input bindings))
95
(defun single-matcher (pattern input bindings)
96
"Call the right function for this kind of single pattern."
97
(funcall (single-match-fn (first pattern))
98
(rest pattern) input bindings))
100
(defun segment-match-fn (x)
101
"Get the segment-match function for x,
102
if it is a symbol that has one."
103
(when (symbolp x) (get x 'segment-match)))
105
(defun single-match-fn (x)
106
"Get the single-match function for x,
107
if it is a symbol that has one."
108
(when (symbolp x) (get x 'single-match)))
110
(defun match-is (var-and-pred input bindings)
111
"Succeed and bind var if the input satisfies pred,
112
where var-and-pred is the list (var pred)."
113
(let* ((var (first var-and-pred))
114
(pred (second var-and-pred))
115
(new-bindings (pat-match var input bindings)))
116
(if (or (eq new-bindings fail)
117
(not (funcall pred input)))
121
(defun match-and (patterns input bindings)
122
"Succeed if all the patterns match the input."
123
(cond ((eq bindings fail) fail)
124
((null patterns) bindings)
125
(t (match-and (rest patterns) input
126
(pat-match (first patterns) input
129
(defun match-or (patterns input bindings)
130
"Succeed if any one of the patterns match the input."
133
(let ((new-bindings (pat-match (first patterns)
135
(if (eq new-bindings fail)
136
(match-or (rest patterns) input bindings)
139
(defun match-not (patterns input bindings)
140
"Succeed if none of the patterns match the input.
141
This will never bind any variables."
142
(if (match-or patterns input bindings)
146
(defun segment-match (pattern input bindings &optional (start 0))
147
"Match the segment pattern ((?* var) . pat) against input."
148
(let ((var (second (first pattern)))
149
(pat (rest pattern)))
151
(match-variable var input bindings)
152
(let ((pos (first-match-pos (first pat) input start)))
156
pat (subseq input pos)
157
(match-variable var (subseq input 0 pos)
159
;; If this match failed, try another longer one
161
(segment-match pattern input bindings (+ pos 1))
164
(defun first-match-pos (pat1 input start)
165
"Find the first position that pat1 could possibly match input,
166
starting at position start. If pat1 is non-constant, then just
168
(cond ((and (atom pat1) (not (match-variable-p pat1)))
169
(position pat1 input :start start :test #'equal))
170
((<= start (length input)) start) ;*** fix, rjf 10/1/92 (was <)
173
(defun segment-match+ (pattern input bindings)
174
"Match one or more elements of input."
175
(segment-match pattern input bindings 1))
177
(defun segment-match? (pattern input bindings)
178
"Match zero or one element of input."
179
(let ((var (second (first pattern)))
180
(pat (rest pattern)))
181
(or (pat-match (cons var pat) input bindings)
182
(pat-match pat input bindings))))
184
(defun match-if (pattern input bindings)
185
"Test an arbitrary expression involving variables.
186
The pattern looks like ((?if code) . rest)."
187
;; *** fix, rjf 10/1/92 (used to eval binding values)
188
(and (progv (mapcar #'car bindings)
189
(mapcar #'cdr bindings)
190
(eval (second (first pattern))))
191
(pat-match (rest pattern) input bindings)))
193
(defun pat-match-abbrev (symbol expansion)
194
"Define symbol as a macro standing for a pat-match pattern."
195
(setf (get symbol 'expand-pat-match-abbrev)
196
(expand-pat-match-abbrev expansion)))
198
(defun expand-pat-match-abbrev (pat)
199
"Expand out all pattern matching abbreviations in pat."
200
(cond ((and (symbolp pat) (get pat 'expand-pat-match-abbrev)))
202
(t (cons (expand-pat-match-abbrev (first pat))
203
(expand-pat-match-abbrev (rest pat))))))
208
(setf (get '?bind 'segment-match) 'bind-match-variable)
210
(defun bind-match-variable (pattern input bindings)
211
"bind a variable to the value of an arbitrary expression.
212
The pattern looks like ((?bind (variable code) ...) . rest)."
214
(destructuring-bind ((?bind . new-bindings) . remaining-pattern) pattern
215
(declare (ignore ?bind))
216
(progv (mapcar #'car bindings)
217
(mapcar #'cdr bindings)
218
(loop for (variable expression) in new-bindings
219
do (setf bindings (extend-bindings variable (eval expression) bindings))) )
220
(pat-match remaining-pattern input bindings)))
225
(defun pat-match (pattern input &optional (bindings no-bindings))
226
"Match pattern against input in the context of the bindings"
227
(cond ((eq bindings fail) fail)
228
((match-variable-p pattern)
229
(match-variable pattern input bindings))
230
((eql pattern input) bindings)
231
((segment-pattern-p pattern)
232
(segment-matcher pattern input bindings))
233
((single-pattern-p pattern) ; ***
234
(single-matcher pattern input bindings)) ; ***
235
((and (consp pattern) (consp input))
236
(pat-match (rest pattern) (rest input)
237
(pat-match (first pattern) (first input)
241
(defun rule-based-translator
242
(input rules &key (matcher #'pat-match)
243
(rule-if #'first) (rule-then #'rest) (action #'sublis))
244
"Find the first rule in rules that matches input,
245
and apply the action to that rule."
248
(let ((result (funcall matcher (funcall rule-if rule)
250
(if (not (eq result fail))
251
(funcall action result (funcall rule-then rule)))))
254
;;; (pat-match '(test ?x (?bind (?y (cons ?x ?x))) ?z) '(test 1 2))