source: trunk/src/TM-SPARQL/sparql_filter.lisp

Last change on this file was 425, checked in by lgiessmann, 13 years ago

TM-SPARQL: fixed a bug in the function in-literal-string-p

File size: 39.2 KB
Line 
1;;+-----------------------------------------------------------------------------
2;;+  Isidorus
3;;+  (c) 2008-2010 Marc Kuester, Christoph Ludwig, Lukas Georgieff
4;;+
5;;+  Isidorus is freely distributable under the LLGPL license.
6;;+  You can find a detailed description in trunk/docs/LLGPL-LICENSE.txt and
7;;+  trunk/docs/LGPL-LICENSE.txt.
8;;+-----------------------------------------------------------------------------
9
10(in-package :TM-SPARQL)
11
12
13(defparameter *supported-functions*
14  (list "BOUND" "isLITERAL" "STR" "DATATYPE" "REGEX")
15  "Contains all supported SPARQL-functions")
16
17
18(defparameter *supported-primary-arithmetic-operators*
19  (list "*" "/") "Contains all supported arithmetic operators.")
20
21
22(defparameter *supported-secundary-arithmetic-operators*
23  (list "+" "-") "Contains all supported arithmetic operators.")
24
25
26(defparameter *supported-compare-operators*
27  (list "!=" "<=" ">=" "=" "<" ">") ;note the order is important!
28                                    ;the operators with length = 2
29                                    ;must be listed first
30  "Contains all supported binary operators.")
31
32
33(defparameter *supported-join-operators*
34  (list "||" "&&") "Contains all supported join operators.")
35
36
37(defparameter *supported-unary-operators*
38  (list "!" "+" "-") "Contains all supported unary operators")
39
40
41(defparameter *allowed-filter-calls*
42  (append (list "one+" "one-" "progn" "or" "and" "not" "!=" "="
43                ">" ">=" "<" "<=" "+" "-" "*" "/")
44          *supported-functions*))
45
46
47(defun *2-compare-operators* ()
48  (remove-null
49   (map 'list #'(lambda(op)
50                  (when (= (length op) 2)
51                    op))
52        *supported-compare-operators*)))
53
54
55(defun *1-compare-operators* ()
56  (remove-null
57   (map 'list #'(lambda(op)
58                  (when (= (length op) 1)
59                    op))
60        *supported-compare-operators*)))
61
62
63(defun *supported-arithmetic-operators* ()
64  (append *supported-primary-arithmetic-operators*
65          *supported-secundary-arithmetic-operators*))
66
67
68(defun *supported-binary-operators* ()
69  (append (*supported-arithmetic-operators*)
70          *supported-compare-operators*
71          *supported-join-operators*))
72
73
74(defun *supported-operators* ()
75  (union (*supported-binary-operators*) *supported-unary-operators*
76         :test #'string=))
77
78
79(defparameter *supported-brackets*
80  (list "(" ")")
81  "Contains all supported brackets in a list of strings.")
82
83
84(defun make-sparql-parser-condition(rest-of-query entire-query expected)
85  "Creates a spqrql-parser-error object."
86  (declare (String rest-of-query entire-query expected))
87  (let ((message
88         (format nil "The query:~%\"~a\"~%~%has a bad token at position ~a => ~a.~%Expected: ~a"
89                 entire-query (- (length entire-query)
90                                 (length rest-of-query))
91                 (subseq entire-query (- (length entire-query)
92                                         (length rest-of-query)))
93                 expected)))
94    (make-condition 'sparql-parser-error :message message)))
95
96
97(defgeneric parse-filter (construct query-string)
98  (:documentation "A helper functions that returns a filter and the next-query
99                   string in the form (:next-query string
100                   :filter-string object).")
101  (:method ((construct SPARQL-Query) (query-string String))
102    ;note the order of the invacations is important!
103    (let* ((result-set-boundings (set-boundings construct query-string))
104           (filter-string (getf result-set-boundings :filter-string))
105           (next-query (getf result-set-boundings :next-query))
106           (original-filter-string
107            (subseq query-string 0 (- (length query-string)
108                                      (length next-query))))
109           (filter-string-casted-constants
110            (cast-literal-constants construct filter-string))
111           (filter-string-unary-ops
112            (set-unary-operators construct filter-string-casted-constants))
113           (filter-string-or-and-ops
114            (set-or-and-operators construct filter-string-unary-ops
115                                  original-filter-string))
116           (filter-string-arithmetic-ops
117            (set-arithmetic-operators construct filter-string-or-and-ops))
118           (filter-string-compare-ops
119            (set-compare-operators construct filter-string-arithmetic-ops))
120           (filter-string-functions
121            (set-functions construct filter-string-compare-ops)))
122      (add-filter construct
123                  (scan-filter-for-deprecated-calls
124                   construct filter-string-functions filter-string))
125      (parse-group construct next-query))))
126
127
128(defgeneric cast-literal-constants (construct filter-string)
129  (:documentation "Casts all constants of the form 'string-value'^^datatype to an
130                   object of the specified type. If the specified type is not
131                   supported the return value is the string-value without a
132                   type specifier.")
133  (:method ((construct SPARQL-Query) (filter-string String))
134    (let ((first-pos (search-first (list "'" "\"") filter-string)))
135      (if (not first-pos)
136          filter-string
137          (let* ((delimiters
138                  (append (white-space) *supported-brackets* (list "}")))
139                 (result (get-literal (subseq filter-string first-pos)))
140                 (literal-value (getf result :literal))
141                 (next-string (getf result :next-string))
142                 (lang
143                  (when (string-starts-with next-string "@")
144                    (let ((end-pos (search-first delimiters next-string)))
145                      (when end-pos
146                        (subseq next-string 0 end-pos)))))
147                 (type
148                  (when (string-starts-with next-string "^^")
149                    (let ((end-pos
150                           (let ((pos (search-first delimiters next-string)))
151                             (if pos
152                                 pos
153                                 (length next-string)))))
154                      (when end-pos
155                        (subseq next-string 2 end-pos)))))
156                 (modified-literal-value
157                  (if type
158                      (if (> (length literal-value) 0)
159                          (string-trim (list (elt literal-value 0)) literal-value)
160                          literal-value)
161                      literal-value)))
162            (concat (subseq filter-string 0 first-pos)
163                    (if type
164                        (write-to-string
165                         (cast-literal modified-literal-value type
166                                       :back-as-string-when-unsupported t))
167                        modified-literal-value)
168                    (cast-literal-constants
169                     construct
170                     (subseq next-string (cond (lang (length lang))
171                                               (type (+ 2 (length type)))
172                                               (t 0))))))))))
173
174
175(defgeneric scan-filter-for-deprecated-calls (construct filter-string
176                                                        original-filter)
177  (:documentation "Returns the passed filter-string where all functions
178                   are explicit wrapped in the filter-functions package
179                   or throws a sparql-parser-error of there is an
180                   unallowed function call.")
181  (:method ((construct SPARQL-Query) (filter-string String)
182            (original-filter String))
183    (let ((result ""))
184      (dotimes (idx (length filter-string) result)
185        (let ((fun-name (return-function-name (subseq filter-string idx))))
186          (cond ((not fun-name)
187                 (push-string (subseq filter-string idx (1+ idx)) result))
188                ((string-starts-with-one-of fun-name *allowed-filter-calls*)
189                 (push-string "(filter-functions::" result)
190                 (push-string fun-name result)
191                 (incf idx (length fun-name)))
192                (t
193                 (error 
194                  (make-condition
195                   'exceptions:sparql-parser-error
196                   :message (format nil "Invalid filter: the filter \"~a\" evaluated to \"~a\" which contains the deprecated function ~a!"
197                                    filter-string original-filter fun-name))))))))))
198
199
200(defun return-function-name (filter-string)
201  "If the string starts with ( there is returned the function name
202   that is placed directly after the (."
203  (declare (String filter-string))
204  (when (string-starts-with filter-string "(")
205    (let ((local-str (trim-whitespace-left (subseq filter-string 1)))
206          (whitespaces (map 'list #'string (white-space)))
207          (result ""))
208      (dotimes (idx (length local-str) result)
209        (let ((current-char (subseq local-str idx (1+ idx))))
210          (if (string-starts-with-one-of
211               current-char (append whitespaces *supported-brackets*))
212              (setf idx (length local-str))
213              (push-string current-char result)))))))
214
215
216(defgeneric set-functions (construct filter-string)
217  (:documentation "Transforms all supported functions of the form
218                   function(x, y) to (function x y).")
219  (:method ((construct SPARQL-Query) (filter-string String))
220    (let ((op-pos (find-functions filter-string)))
221      (if (not op-pos)
222          filter-string
223          (let* ((fun-name
224                  (return-if-starts-with (subseq filter-string op-pos)
225                                         *supported-functions*))
226                 (left-str (subseq filter-string 0 op-pos))
227                 (right-str (subseq filter-string
228                                    (+ op-pos (length fun-name))))
229                 (cleaned-right-str (trim-whitespace-left right-str))
230                 (arg-list (bracket-scope cleaned-right-str))
231                 (cleaned-arg-list (clean-function-arguments arg-list))
232                 (modified-str
233                  (let ((modified-arg-list
234                         (if (string= fun-name "BOUND")
235                             (let* ((var-start
236                                     (search-first (list "?" "$") cleaned-arg-list))
237                                    (var-end
238                                     (let ((val
239                                            (when var-start
240                                              (search-first
241                                               (list ")")
242                                               (subseq cleaned-arg-list var-start)))))
243                                       (if val
244                                           val
245                                           (length (subseq cleaned-arg-list var-start))))))
246                               (when (and var-start var-end)
247                                 (concat (subseq cleaned-arg-list 0 var-start)
248                                         "\"" (subseq cleaned-arg-list var-start
249                                                      (+ var-start var-end))
250                                         "\"" (subseq cleaned-arg-list
251                                                      (+ var-start var-end)))))
252                             cleaned-arg-list)))
253                    (concat
254                     left-str "(" fun-name " " modified-arg-list ")"
255                     (subseq right-str (+ (- (length right-str)
256                                             (length cleaned-right-str))
257                                          (length arg-list)))))))
258            (set-functions construct modified-str))))))
259
260
261(defun clean-function-arguments (argument-string)
262  "Transforms all arguments within an argument list of the form
263   (x, y, z, ...) to x y z."
264  (declare (String argument-string))
265  (when (and (string-starts-with argument-string "(")
266             (string-ends-with argument-string ")"))
267    (let ((local-str (subseq argument-string 1 (1- (length argument-string))))
268          (result ""))
269      (dotimes (idx (length local-str) result)
270        (let ((current-char (subseq local-str idx (1+ idx))))
271          (if (and (string= current-char ",")
272                   (not (in-literal-string-p local-str idx)))
273              (push-string " " result)
274              (push-string current-char result)))))))
275
276
277(defun find-functions (filter-string)
278  "Returns the idx of the first found 'BOUND', 'isLITERAL', 'STR',
279   'DATATYPE', or 'REGEX'.
280   It must not be in a literal string or directly after a (."
281  (declare (String filter-string))
282  (let* ((first-pos
283          (search-first-ignore-literals *supported-functions*
284                                        filter-string)))
285    (when first-pos
286      (let ((left-part (trim-whitespace-right (subseq filter-string 0 first-pos))))
287        (if (not (string-ends-with left-part "("))
288            first-pos
289            (let ((next-pos
290                   (find-functions (subseq filter-string (1+ first-pos)))))
291              (when next-pos
292                (+ 1 first-pos next-pos))))))))
293
294
295(defgeneric set-compare-operators (construct filter-string)
296  (:documentation "Transforms the =, !=, <, >, <= and >= operators in the
297                   filter string to the the corresponding lisp functions.")
298  (:method ((construct SPARQL-Query) (filter-string String))
299    (let ((op-pos (find-compare-operators filter-string)))
300      (if (not op-pos)
301          filter-string
302          (let* ((op-str (if (string-starts-with-one-of
303                              (subseq filter-string op-pos)
304                              (*2-compare-operators*))
305                             (subseq filter-string op-pos (+ 2 op-pos))
306                             (subseq filter-string op-pos (1+ op-pos))))
307                 (left-str (subseq filter-string 0 op-pos))
308                 (right-str (subseq filter-string (+ (length op-str) op-pos)))
309                 (left-scope (find-compare-left-scope left-str))
310                 (right-scope (find-compare-right-scope right-str))
311                 (modified-str
312                  (concat (subseq left-str 0 (- (length left-str)
313                                                (length left-scope)))
314                          "(" op-str " " left-scope " " right-scope ")"
315                          (subseq right-str (length right-scope)))))
316            (set-compare-operators construct modified-str))))))
317
318
319(defun find-compare-operators (filter-string)
320  "Returns the idx of the first found =, !=, <, >, <= or >= operator.
321   It must not be in a literal string or directly after a (."
322  (declare (String filter-string))
323  (let* ((first-pos
324          (search-first-ignore-literals *supported-compare-operators*
325                                        filter-string))
326         (delta (if first-pos
327                    (if (string-starts-with-one-of
328                         (subseq filter-string first-pos)
329                         (*2-compare-operators*))
330                        2
331                        1)
332                    1)))
333    (when first-pos
334      (let ((left-part (trim-whitespace-right (subseq filter-string 0 first-pos))))
335        (if (not (string-ends-with-one-of
336                  left-part (append (*1-compare-operators*) (list "("))))
337            first-pos
338            (let ((next-pos
339                   (find-compare-operators (subseq filter-string (+ delta first-pos)))))
340              (when next-pos
341                (+ delta first-pos next-pos))))))))
342
343
344(defun find-compare-left-scope (left-string)
345  "Returns the string that is the left part of the binary scope."
346  (declare (String left-string))
347  (let* ((first-bracket
348          (let ((inner-value (search-first-unclosed-paranthesis left-string)))
349            (when inner-value
350              (+ inner-value (1+ (length (name-after-paranthesis
351                                          (subseq left-string inner-value))))))))
352         (paranthesis-pair-idx
353          (let ((value
354                 (let* ((cleaned-str (trim-whitespace-right left-string))
355                        (bracket-scope (reverse-bracket-scope cleaned-str)))
356                   (when bracket-scope
357                     (- (- (length left-string)
358                           (- (length left-string) (length cleaned-str)))
359                        (length bracket-scope))))))
360            (when value ;search a functionname: FUN(...)
361              (let* ((str-before (subseq left-string 0 value))
362                     (c-str-before (trim-whitespace-right str-before)))
363                (if (string-ends-with-one-of c-str-before *supported-functions*)
364                    (loop for fun-name in *supported-functions*
365                       when (string-ends-with c-str-before fun-name)
366                       return (- value
367                                 (+ (- (length str-before)
368                                       (length c-str-before))
369                                    (length fun-name))))
370                    value)))))
371         (start-idx (or first-bracket paranthesis-pair-idx 0)))
372    (subseq left-string start-idx)))
373
374
375(defun find-compare-right-scope (right-string)
376  "Returns the string that is the right part of the binary scope."
377  (declare (String right-string))
378  (let* ((first-pos
379          (search-first-ignore-literals *supported-compare-operators*
380                                        right-string))
381         (first-bracket
382          (let ((inner-value (search-first-unopened-paranthesis right-string)))
383            (when inner-value (1+ inner-value))))
384         (paranthesis-pair-idx
385          (let* ((cleaned-str (trim-whitespace-left right-string))
386                 (bracket-scope (bracket-scope cleaned-str)))
387            (when bracket-scope
388              (+ (- (length right-string) (length cleaned-str))
389                 (length bracket-scope)))))
390         (end-idx (cond (paranthesis-pair-idx
391                         paranthesis-pair-idx)
392                        ((and first-pos first-bracket)
393                         (min first-pos first-bracket))
394                        (first-pos first-pos)
395                        (first-bracket first-bracket)
396                        (t (if (= (length right-string) 0)
397                               0
398                               (length right-string))))))
399    (subseq right-string 0 end-idx)))
400
401
402(defgeneric set-arithmetic-operators (construct filter-string)
403  (:documentation "Transforms the +, -, *, / operators in the filter
404                   string to the the corresponding lisp functions.")
405  (:method ((construct SPARQL-Query) (filter-string String))
406    (let ((filter-string-*/ (set-*-and-/-operators construct filter-string)))
407      (set-+-and---operators construct filter-string-*/))))
408
409
410(defun find-*/-operators (filter-string)
411  "Returns the idx of the first found * or / operator.
412   It must not be in a literal string or directly after a (."
413  (declare (String filter-string))
414  (let ((first-pos
415         (search-first-ignore-literals *supported-primary-arithmetic-operators*
416                                       filter-string)))
417    (when first-pos
418      (let ((left-part (trim-whitespace-right (subseq filter-string 0 first-pos))))
419        (if (not (string-ends-with left-part "("))
420            first-pos
421            (let ((next-pos
422                   (find-*/-operators (subseq filter-string (1+ first-pos)))))
423              (when next-pos
424                (+ 1 first-pos next-pos))))))))
425
426
427(defgeneric set-*-and-/-operators (construct filter-string)
428  (:documentation "Transforms the *, / operators in the filter
429                   string to the the corresponding lisp functions.")
430  (:method ((construct SPARQL-Query) (filter-string String))
431    (let ((op-pos (find-*/-operators filter-string)))
432      (if (not op-pos)
433          filter-string
434          (let* ((op-str (subseq filter-string op-pos (1+ op-pos)))
435                 (left-str (subseq filter-string 0 op-pos))
436                 (right-str (subseq filter-string (1+ op-pos)))
437                 (left-scope (find-*/-left-scope left-str))
438                 (right-scope (find-*/-right-scope right-str))
439                 (modified-str
440                  (concat
441                   (subseq left-str 0 (- (length left-str)
442                                         (length left-scope)))
443                   "(" op-str " " left-scope " " right-scope ")"
444                   (subseq right-str (length right-scope)))))
445            (set-*-and-/-operators construct modified-str))))))
446
447
448(defun find-*/-left-scope (left-string)
449  "Returns the string that is the left part of the binary scope."
450  (declare (String left-string))
451  (let* ((first-bracket
452          (let ((inner-value (search-first-unclosed-paranthesis left-string)))
453            (when inner-value
454              (+ inner-value (1+ (length (name-after-paranthesis
455                                          (subseq left-string inner-value))))))))
456         (other-anchor
457          (let ((inner-value
458                 (search-first-ignore-literals
459                  (append *supported-secundary-arithmetic-operators*
460                          *supported-compare-operators*)
461                  left-string :from-end t)))
462            (when inner-value
463              (1+ inner-value))))
464         (paranthesis-pair-idx
465          (let* ((cleaned-str (trim-whitespace-right left-string))
466                 (bracket-scope (reverse-bracket-scope cleaned-str)))
467            (when bracket-scope
468              (- (- (length left-string)
469                    (- (length left-string) (length cleaned-str)))
470                 (length bracket-scope)))))
471         (start-idx (cond (paranthesis-pair-idx
472                           paranthesis-pair-idx)
473                          ((and first-bracket other-anchor)
474                           (max first-bracket other-anchor))
475                          ((or first-bracket other-anchor)
476                           (or first-bracket other-anchor))
477                          (t 0))))
478    (subseq left-string start-idx)))
479
480
481(defun find-*/-right-scope (right-string)
482  "Returns the string that is the right part of the binary scope."
483  (declare (String right-string))
484  (let* ((first-pos (search-first-ignore-literals
485                     (append (*supported-arithmetic-operators*)
486                             *supported-compare-operators*)
487                     right-string))
488         (first-bracket
489          (let ((inner-value (search-first-unopened-paranthesis right-string)))
490            (when inner-value (1+ inner-value))))
491         (paranthesis-pair-idx
492          (let* ((cleaned-str (trim-whitespace-left right-string))
493                 (bracket-scope (bracket-scope cleaned-str)))
494            (when bracket-scope
495              (+ (- (length right-string) (length cleaned-str))
496                 (length bracket-scope)))))
497         (end-idx (cond (paranthesis-pair-idx
498                         paranthesis-pair-idx)
499                        ((and first-pos first-bracket)
500                         (min first-pos first-bracket))
501                        (first-pos first-pos)
502                        (first-bracket first-bracket)
503                        (t (if (= (length right-string) 0)
504                               (1- (length right-string)))))))
505    (subseq right-string 0 end-idx)))
506
507
508(defgeneric set-+-and---operators (construct filter-string)
509  (:documentation "Transforms the +, - operators in the filter
510                   string to the the corresponding lisp functions.")
511  (:method ((construct SPARQL-Query) (filter-string String))
512    (let ((op-pos (find-+--operators filter-string)))
513      (if (not op-pos)
514          filter-string
515          (let* ((op-str (subseq filter-string op-pos (1+ op-pos)))
516                 (left-str (subseq filter-string 0 op-pos))
517                 (right-str (subseq filter-string (1+ op-pos)))
518                 (left-scope (find-+--left-scope left-str))
519                 (right-scope (find-+--right-scope right-str))
520                 (modified-str
521                  (concat (subseq left-str 0 (- (length left-str)
522                                                (length left-scope)))
523                          "(" op-str " " left-scope " " right-scope ")"
524                          (subseq right-str (length right-scope)))))
525            (set-+-and---operators construct modified-str))))))
526
527
528(defun find-+--left-scope (left-string)
529  "Returns the string that is the left part of the binary scope."
530  (declare (String left-string))
531  (let* ((first-bracket
532          (let ((inner-value (search-first-unclosed-paranthesis left-string)))
533            (when inner-value
534              (+ inner-value (1+ (length (name-after-paranthesis
535                                          (subseq left-string inner-value))))))))
536         (other-anchor
537          (let ((inner-value
538                 (search-first-ignore-literals *supported-compare-operators*
539                                               left-string :from-end t)))
540            (when inner-value
541              (1+ inner-value))))
542         (paranthesis-pair-idx
543          (let* ((cleaned-str (trim-whitespace-right left-string))
544                 (bracket-scope (reverse-bracket-scope cleaned-str)))
545            (when bracket-scope
546              (- (- (length left-string)
547                    (- (length left-string) (length cleaned-str)))
548                 (length bracket-scope)))))
549         (start-idx (cond (paranthesis-pair-idx
550                           paranthesis-pair-idx)
551                          ((and first-bracket other-anchor)
552                           (max first-bracket other-anchor))
553                          ((or first-bracket other-anchor)
554                           (or first-bracket other-anchor))
555                          (t 0))))
556    (subseq left-string start-idx)))
557
558
559(defun find-+--right-scope (right-string)
560  "Returns the string that is the right part of the binary scope."
561  (declare (String right-string))
562  (let* ((first-pos (search-first-ignore-literals
563                     (append (*supported-arithmetic-operators*)
564                             *supported-compare-operators*)
565                     right-string))
566         (first-bracket
567          (let ((inner-value (search-first-unopened-paranthesis right-string)))
568            (when inner-value (1+ inner-value))))
569         (paranthesis-pair-idx
570          (let* ((cleaned-str (trim-whitespace-left right-string))
571                 (bracket-scope (bracket-scope cleaned-str)))
572            (when bracket-scope
573              (+ (- (length right-string) (length cleaned-str))
574                 (length bracket-scope)))))
575         (end-idx (cond (paranthesis-pair-idx
576                         paranthesis-pair-idx)
577                        ((and first-pos first-bracket)
578                         (min first-pos first-bracket))
579                        (first-pos first-pos)
580                        (first-bracket first-bracket)
581                        (t (if (= (length right-string) 0)
582                               (1- (length right-string)))))))
583    (subseq right-string 0 end-idx)))
584
585
586(defun find-+--operators (filter-string)
587  "Returns the idx of the first found + or - operator.
588   It must not be in a literal string or directly after a (."
589  (declare (String filter-string))
590  (let ((first-pos
591         (search-first-ignore-literals *supported-secundary-arithmetic-operators*
592                                       filter-string)))
593    (when first-pos
594      (let ((left-part (trim-whitespace-right (subseq filter-string 0 first-pos))))
595        (if (and (not (string-ends-with left-part "(one"))
596                 (not (string-ends-with left-part "(")))
597            first-pos
598            (let ((next-pos
599                   (find-+--operators (subseq filter-string (1+ first-pos)))))
600              (when next-pos
601                (+ 1 first-pos next-pos))))))))
602
603
604(defgeneric set-or-and-operators (construct filter-string original-filter-string)
605  (:documentation "Transforms the || and && operators in the filter string to
606                   the the lisp or and and functions.")
607  (:method ((construct SPARQL-Query) (filter-string String)
608            (original-filter-string String))
609    (let ((op-pos (search-first-ignore-literals
610                   *supported-join-operators* filter-string)))
611      (if (not op-pos)
612          filter-string
613          (let* ((op-str (subseq filter-string op-pos (+ 2 op-pos)))
614                 (left-str (subseq filter-string 0 op-pos))
615                 (right-str (subseq filter-string (+ (length op-str) op-pos)))
616                 (left-scope (find-or-and-left-scope left-str))
617                 (right-scope (find-or-and-right-scope right-str))
618                 (modified-str
619                  (concat (subseq left-str 0 (- (length left-str)
620                                                (length left-scope)))
621                          "(" (if (string= op-str "||") "or" "and") " "
622                          "(progn " left-scope ")" "(progn " right-scope ")) "
623                          (subseq right-str (length right-scope)))))
624            (when (or (= (length (trim-whitespace left-scope)) 0)
625                      (= (length (trim-whitespace right-scope)) 0))
626              (error (make-condition
627                      'sparql-parser-error
628                      :message (format nil "Invalid filter: \"~a\", expect an RDF term after and before: \"~a\"" original-filter-string op-str))))
629            (set-or-and-operators construct modified-str original-filter-string))))))
630
631
632(defun find-binary-op-string (filter-string idx)
633  "Returns the operator as string that is placed on the position idx."
634  (let* ((2-ops
635          (remove-null (map 'list #'(lambda(op-string)
636                                      (when (= (length op-string) 2)
637                                        op-string))
638                            (*supported-binary-operators*))))
639         (operator-str (subseq filter-string idx)))
640    (if (string-starts-with-one-of operator-str 2-ops)
641        (subseq operator-str 0 2)
642        (subseq operator-str 0 1))))
643
644
645(defun find-or-and-left-scope (left-string)
646  "Returns the string that is the left part of the binary scope."
647  (declare (String left-string))
648  (let* ((first-bracket
649          (let ((inner-value (search-first-unclosed-paranthesis left-string)))
650            (when inner-value 
651              (+ inner-value (1+ (length (name-after-paranthesis
652                                          (subseq left-string inner-value))))))))
653         
654         (start-idx (if first-bracket
655                        first-bracket
656                        0)))
657    (subseq left-string start-idx)))
658
659
660(defun name-after-paranthesis (str)
661  "Returns the substring that is contained after the paranthesis.
662   str must start with a ( otherwise the returnvalue is nil."
663  (declare (String str))
664  (let ((result "")
665        (non-whitespace-found nil))
666  (when (string-starts-with str "(")
667    (let ((cleaned-str (subseq str 1)))
668      (dotimes (idx (length cleaned-str))
669        (let ((current-char (subseq cleaned-str idx (1+ idx))))
670          (cond ((string-starts-with-one-of current-char (list "(" ")"))
671                 (setf idx (length cleaned-str)))
672                ((and non-whitespace-found
673                      (white-space-p current-char))
674                 (setf idx (length cleaned-str)))
675                ((white-space-p current-char)
676                 (push-string current-char result))
677                (t
678                 (push-string current-char result)
679                 (setf non-whitespace-found t)))))
680      result))))
681
682
683(defun find-or-and-right-scope (right-string)
684  "Returns the string that is the right part of the binary scope."
685  (declare (String right-string))
686  (let* ((first-pos (search-first-ignore-literals
687                     *supported-join-operators* right-string))
688         (first-bracket
689          (let ((inner-value (search-first-unopened-paranthesis right-string)))
690            (when inner-value (1+ inner-value))))
691         (paranthesis-pair-idx
692          (let* ((cleaned-str (trim-whitespace-left right-string))
693                 (bracket-scope (bracket-scope cleaned-str)))
694            (when bracket-scope
695              (+ (- (length right-string) (length cleaned-str))
696                 (length bracket-scope)))))
697         (end-idx
698          (cond ((and first-pos first-bracket)
699                 (if (< first-pos first-bracket)
700                     (if paranthesis-pair-idx
701                         (if (< first-pos paranthesis-pair-idx)
702                             paranthesis-pair-idx
703                             first-pos)
704                         first-pos)
705                     first-bracket))
706                (first-bracket first-bracket)
707                (first-pos
708                 (if paranthesis-pair-idx
709                     (if (< first-pos paranthesis-pair-idx)
710                         paranthesis-pair-idx
711                         first-pos)
712                     first-pos))
713                (t
714                 (if (= (length right-string) 0)
715                     0
716                     (length right-string))))))
717    (subseq right-string 0 end-idx)))
718
719
720(defgeneric set-unary-operators (construct filter-string)
721  (:documentation "Transforms the unary operators !, +, - to (not ),
722                   (one+ ) and (one- ). The return value is a modified filter
723                   string.")
724  (:method ((construct SPARQL-Query) (filter-string String))
725    (let ((result-string ""))
726      (dotimes (idx (length filter-string))
727        (let ((current-char (subseq filter-string idx (1+ idx))))
728          (cond ((string= current-char "!")
729                 (if (and (< idx (1- (length filter-string)))
730                          (string= (subseq filter-string (1+ idx) (+ 2 idx)) "="))
731                     (push-string current-char result-string)
732                     (let ((result (unary-operator-scope filter-string idx)))
733                       (push-string "(not " result-string)
734                       (push-string (set-unary-operators construct (getf result :scope))
735                                    result-string)
736                       (push-string ")" result-string)
737                       (setf idx (- (1- (length filter-string))
738                                    (length (getf result :next-query)))))))
739                ((or (string= current-char "-")
740                     (string= current-char "+"))
741                 (let ((string-before
742                        (trim-whitespace-right (subseq filter-string 0 idx))))
743                   (if (or (string= string-before "")
744                           (string-ends-with string-before "(progn")
745                           (string-ends-with-one-of
746                            string-before (append (*supported-operators*) (list "("))))
747                       (let ((result (unary-operator-scope filter-string idx)))
748                         (push-string (concat "(one" current-char " ")
749                                      result-string)
750                         (push-string (set-unary-operators construct
751                                                           (getf result :scope))
752                                      result-string)
753                         (push-string ")" result-string)
754                         (setf idx (- (1- (length filter-string))
755                                      (length (getf result :next-query)))))
756                       (push-string current-char result-string))))
757                ((or (string= current-char "'")
758                     (string= current-char "\""))
759                 (let ((literal
760                        (get-literal (subseq filter-string idx))))
761                   (if literal
762                       (progn
763                         (setf idx (- (1- (length filter-string))
764                                      (length (getf literal :next-string))))
765                         (push-string (getf literal :literal) result-string))
766                       (push-string current-char result-string))))
767                (t
768                 (push-string current-char result-string)))))
769      result-string)))
770
771
772(defun unary-operator-scope (filter-string idx)
773  "Returns a list of the form (:next-query <string> :scope <string>).
774   scope contains the statement that is in the scope of one of the following
775   operators !, +, -."
776  (declare (String filter-string)
777           (Integer idx))
778  (let* ((string-after (subseq filter-string (1+ idx)))
779         (cleaned-str (trim-whitespace-left string-after)))
780    (cond ((string-starts-with cleaned-str "(")
781           (let ((result (bracket-scope cleaned-str)))
782             (list :next-query (string-after cleaned-str result)
783                   :scope result)))
784          ((or (string-starts-with cleaned-str "?")
785               (string-starts-with cleaned-str "$"))
786           (let ((result (get-filter-variable cleaned-str)))
787             (list :next-query (string-after cleaned-str result)
788                   :scope result)))
789          ((string-starts-with cleaned-str "\"")
790           (let ((result (get-literal cleaned-str :quotation "\"")))
791             (list :next-query (getf result :next-string)
792                   :scope (getf result :literal))))
793          ((string-starts-with-digit cleaned-str)
794           (let ((result (separate-leading-digits cleaned-str)))
795             (list :next-query (string-after cleaned-str result)
796                   :scope result)))
797          ((string-starts-with cleaned-str "true")
798           (list :next-query (string-after cleaned-str "true")
799                 :scope "true"))
800          ((string-starts-with cleaned-str "false")
801           (list :next-query (string-after cleaned-str "false")
802                 :scope "false"))
803          ((let ((pos (search-first *supported-functions* cleaned-str)))
804             (when pos
805               (= pos 0)))
806           (let ((result (function-scope cleaned-str)))
807             (list :next-query (string-after cleaned-str result)
808                   :scope result)))
809          (t
810           (error
811            (make-condition
812             'sparql-parser-error
813             :message
814             (format
815              nil "Invalid filter: \"~a\". An unary operator must be followed by ~a"
816              filter-string
817              "a number, boolean, string, function or a variable")))))))
818
819
820(defun function-scope (str)
821  "If str starts with a supported function there is given the entire substr
822   that is the scope of the function, i.e. the function name and all its
823   variable including the closing )."
824  (declare (String str))
825  (let* ((cleaned-str (trim-whitespace-left str))
826         (after-fun
827          (remove-null (map 'list #'(lambda(fun)
828                                      (when (string-starts-with cleaned-str fun)
829                                        (string-after str fun)))
830                            *supported-functions*)))
831         (fun-suffix (when after-fun
832                       (trim-whitespace-left (first after-fun)))))
833    (when fun-suffix
834      (let* ((args (bracket-scope fun-suffix))
835             (fun-name (string-until cleaned-str args)))
836        (concat fun-name args)))))
837
838
839(defun get-filter-variable (str)
840  "Returns the substring of str if str starts with ? or $ until the variable ends,
841   otherwise the return value is nil."
842  (declare (String str))
843  (when (or (string-starts-with str "?")
844            (string-starts-with str "$"))
845    (let ((found-end (search-first (append (white-space) (*supported-operators*)
846                                           *supported-brackets* (list "?" "$"))
847                                   (subseq str 1))))
848      (if found-end
849          (subseq str 0 (1+ found-end))
850          str))))
851
852
853(defun reverse-bracket-scope (str &key (open-bracket "(") (close-bracket ")"))
854  "If str ends with close-bracket there will be returned the substring until
855   the matching open-bracket is found. Otherwise the return value is nil."
856  (declare (String str open-bracket close-bracket))
857  (when (string-ends-with str close-bracket)
858    (let ((local-str (subseq str 0 (1- (length str))))
859          (result ")")
860          (close-brackets 1))
861      (do ((idx (1- (length local-str)))) ((< idx 0))
862        (let ((current-char (subseq local-str idx (1+ idx))))
863          (push-string current-char result)
864          (cond ((string= current-char open-bracket)
865                 (when (not (in-literal-string-p local-str idx))
866                   (decf close-brackets))
867                 (when (= close-brackets 0)
868                   (setf idx 0)))
869                ((string= current-char close-bracket)
870                 (when (not (in-literal-string-p local-str idx))
871                   (incf close-brackets)))))
872        (decf idx))
873      (reverse result))))
874
875
876(defun bracket-scope (str &key (open-bracket "(") (close-bracket ")"))
877  "If str starts with open-bracket there will be returned the substring until
878   the matching close-bracket is found. Otherwise the return value is nil."
879  (declare (String str open-bracket close-bracket))
880  (when (string-starts-with str open-bracket)
881    (let ((open-brackets 0)
882          (result ""))
883      (dotimes (idx (length str))
884        (let ((current-char (subseq str idx (1+ idx))))
885          (cond ((or (string= "'" current-char)
886                     (string= "\"" current-char))
887                 (let ((literal (get-literal (subseq str idx))))
888                   (if literal
889                       (progn
890                         (setf idx (- (1- (length str))
891                                      (length (getf literal :next-string))))
892                         (push-string (getf literal :literal) result))
893                       (progn
894                         (setf result nil)
895                         (setf idx (length str))))))
896                ((string= current-char close-bracket)
897                 (decf open-brackets)
898                 (push-string current-char result)
899                 (when (= open-brackets 0)
900                   (setf idx (length str))))
901                ((string= current-char open-bracket)
902                 (incf open-brackets)
903                 (push-string current-char result))
904                (t
905                 (push-string current-char result)))))
906      result)))
907
908
909(defgeneric set-boundings (construct query-string)
910  (:documentation "Returns a list of the form (:next-query <string>
911                   :filter-string <string>). next-query is a string containing
912                   the query after the filter and filter is a string
913                   containing the actual filter. Additionally all free
914                   '(' are transformed into '(progn' and all ', ''', \"\"\"
915                   are transformed into \".")
916  (:method ((construct SPARQL-Query) (query-string String))
917    (let ((filter-string "")
918          (open-brackets 0)
919          (result nil))
920      (dotimes (idx (length query-string))
921        (let ((current-char (subseq query-string idx (1+ idx))))
922          (cond ((string= "(" current-char)
923                 (setf open-brackets (1+ open-brackets))
924                 (if (progn-p query-string idx)
925                     (push-string "(progn " filter-string)
926                     (push-string current-char filter-string)))
927                ((string= ")" current-char)
928                 (setf open-brackets (1- open-brackets))
929                 (when (< open-brackets 0)
930                   (error
931                    (make-sparql-parser-condition
932                     (subseq query-string idx)
933                     (original-query construct)
934                     "an opening bracket \"(\" is missing for the current closing one")))
935                 (push-string current-char filter-string))
936                ((or (string= "'" current-char)
937                     (string= "\"" current-char))
938                 (let ((result
939                        (get-literal (subseq query-string idx) :quotation "\"")))
940                   (unless result
941                     (error (make-sparql-parser-condition
942                             (subseq query-string idx)
943                             (original-query construct)
944                             "a closing character for the given literal")))
945                   (setf idx (- (1- (length query-string))
946                                (length (getf result :next-string))))
947                   (push-string (getf result :literal) filter-string)))
948                ((and (string= current-char (string #\newline))
949                      (= 0 open-brackets))
950                 (setf result
951                       (list :next-query (subseq query-string idx)
952                             :filter-string filter-string))
953                 (setf idx (1- (length query-string))))
954                ((string= current-char "}")
955                 (when (/= open-brackets 0)
956                   (error (make-sparql-parser-condition
957                           (subseq query-string idx)
958                           (original-query construct)
959                           (format nil
960                                   "a valid filter, but the filter is not complete, ~a"
961                                   (if (> open-brackets 0)
962                                       (format nil "~a ')' is missing"
963                                               open-brackets)
964                                       (format nil "~a '(' is missing"
965                                               open-brackets))))))
966                 (setf result
967                       (list :next-query (subseq query-string idx)
968                             :filter-string filter-string)))
969                (t
970                 (push-string current-char filter-string)))))
971      result)))
972
973
974(defun progn-p(query-string idx)
975  "Returns t if the ( at position idx in the filter string
976  represents a (progn) block."
977  (declare (String query-string)
978           (Integer idx))
979  (let* ((delimiters (append (list " " "," (string #\Space) (string #\Tab)
980                                   (string #\Newline) (string #\cr) "(" ")")
981                             (*supported-operators*)))
982         (string-before (trim-whitespace-right (subseq query-string 0 idx)))
983         (fragment-before-idx
984          (search-first delimiters string-before :from-end t))
985         (fragment-before
986          (if (and (not fragment-before-idx)
987                   (and (> (length string-before) 0)
988                        (not (string-ends-with-one-of
989                              (trim-whitespace-right string-before)
990                              *supported-functions*))))
991              (error (make-condition
992                      'SPARQL-PARSER-ERROR
993                      :message (format nil "Invalid filter: \"~a\"~%"
994                                       query-string)))
995              (if fragment-before-idx
996                  (subseq string-before fragment-before-idx)
997                  nil))))
998    (when fragment-before
999      (mapcan #'(lambda(operator)
1000                  (when (and (string-starts-with fragment-before operator)
1001                             (> (length fragment-before) (length operator)))
1002                    (setf fragment-before
1003                          (string-after fragment-before operator))))
1004              (append (*supported-operators*) *supported-brackets*)))
1005    (if fragment-before
1006        (progn
1007          (when (or (string-starts-with fragment-before "?")
1008                    (string-starts-with fragment-before "$"))
1009            (error
1010             (make-condition
1011              'SPARQL-PARSER-ERROR
1012              :message (format nil "Invalid filter: found \"~a\" but expected ~a"
1013                               fragment-before *supported-functions*))))
1014          (when (not (string-starts-with-one-of
1015                      fragment-before (append *supported-functions* delimiters)))
1016            (error
1017             (make-condition
1018              'SPARQL-PARSER-ERROR
1019              :message
1020              (format nil "Invalid character: \"~a\", expected characters: ~a"
1021                      fragment-before (append *supported-functions* delimiters)))))
1022          (if (string-ends-with-one-of fragment-before *supported-functions*)
1023              nil
1024              t))
1025        (if (find string-before *supported-functions* :test #'string=)
1026            nil
1027            t))))
1028
1029
1030(defun get-variables-from-filter-string(filter-string)
1031  "Returns a list of string with all variables that are used in this filter."
1032  (let ((variables nil))
1033    (dotimes (idx (length filter-string))
1034      (let ((current-string (subseq filter-string idx)))
1035        (cond ((and (or (string-starts-with current-string "?")
1036                        (string-starts-with current-string "$"))
1037                    (not (in-literal-string-p filter-string idx)))
1038               (let ((end-pos
1039                      (let ((inner-value
1040                             (search-first
1041                              (append (list " " "?" "$" "." ",")
1042                                      (*supported-operators*)
1043                                      *supported-brackets*
1044                                      (map 'list #'string (white-space)))
1045                              (subseq current-string 1))))
1046                        (if inner-value
1047                            (1+ inner-value)
1048                            (length current-string)))))
1049                 (push (subseq current-string 1 end-pos) variables)
1050                 (incf idx end-pos)))
1051              ;BOUND needs a separate hanlding since all variables
1052              ;      were written into strings so they have to be
1053              ;      searched different
1054              ((and (string-starts-with current-string "BOUND ")
1055                    (not (in-literal-string-p filter-string idx)))
1056               (let* ((next-str (subseq current-string (length "BOUND ")))
1057                      (literal (when (string-starts-with next-str "\"")
1058                                 (let ((val (get-literal next-str)))
1059                                   (when val
1060                                     (getf val :literal))))))
1061                 (when (and literal (> (length literal) 3)) ;"?.." | "$.."
1062                   (push (subseq (string-trim (list #\") literal) 1) variables))
1063                 (incf idx (+ (length "BOUND ") (length literal))))))))
1064    (remove-duplicates variables :test #'string=)))
Note: See TracBrowser for help on using the repository browser.