| 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 | (defun parse-closed-value(query-string query-object &key (open "<") (close ">")) |
|---|
| 13 | "A helper function that checks the value of a statement within |
|---|
| 14 | two brackets, i.e. <prefix-value>. A list of the |
|---|
| 15 | form (:next-query string :value string) is returned." |
|---|
| 16 | (declare (String query-string open close) |
|---|
| 17 | (SPARQL-Query query-object)) |
|---|
| 18 | (let ((trimmed-string (cut-comment query-string))) |
|---|
| 19 | (if (string-starts-with trimmed-string open) |
|---|
| 20 | (let* ((pref-url (string-until (string-after trimmed-string open) close)) |
|---|
| 21 | (next-query-str (string-after trimmed-string close))) |
|---|
| 22 | (unless next-query-str |
|---|
| 23 | (error (make-sparql-parser-condition |
|---|
| 24 | trimmed-string (original-query query-object) |
|---|
| 25 | close))) |
|---|
| 26 | (list :next-query next-query-str |
|---|
| 27 | :value pref-url)) |
|---|
| 28 | (error (make-sparql-parser-condition |
|---|
| 29 | trimmed-string (original-query query-object) |
|---|
| 30 | close))))) |
|---|
| 31 | |
|---|
| 32 | |
|---|
| 33 | (defun cut-comment (query-string) |
|---|
| 34 | "Returns the given string back. If the query starts with a # or |
|---|
| 35 | space # the characters until the nextline are removed." |
|---|
| 36 | (declare (String query-string)) |
|---|
| 37 | (let ((trimmed-str (trim-whitespace-left query-string))) |
|---|
| 38 | (if (string-starts-with trimmed-str "#") |
|---|
| 39 | (let ((next-query (string-after trimmed-str (string #\newline)))) |
|---|
| 40 | (if next-query |
|---|
| 41 | (let ((cleaned-next-query (cut-comment next-query))) |
|---|
| 42 | (if (string= next-query cleaned-next-query) |
|---|
| 43 | next-query |
|---|
| 44 | (cut-comment next-query))) |
|---|
| 45 | "")) |
|---|
| 46 | trimmed-str))) |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | (defgeneric parser-start(construct query-string) |
|---|
| 50 | (:documentation "The entry point of the SPARQL-parser.") |
|---|
| 51 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 52 | (let ((trimmed-query-string (cut-comment query-string))) |
|---|
| 53 | (cond ((string-starts-with trimmed-query-string "SELECT") |
|---|
| 54 | (parse-select |
|---|
| 55 | construct (string-after trimmed-query-string "SELECT"))) |
|---|
| 56 | ((string-starts-with trimmed-query-string "PREFIX") |
|---|
| 57 | (parse-prefixes |
|---|
| 58 | construct (string-after trimmed-query-string "PREFIX"))) |
|---|
| 59 | ((string-starts-with trimmed-query-string "BASE") |
|---|
| 60 | (parse-base construct (string-after trimmed-query-string "BASE") |
|---|
| 61 | #'parser-start)) |
|---|
| 62 | ((= (length trimmed-query-string) 0) |
|---|
| 63 | ;; If there is only a BASE and/or PREFIX statement return a |
|---|
| 64 | ;; query-object with the result nil |
|---|
| 65 | construct) |
|---|
| 66 | (t |
|---|
| 67 | (error (make-sparql-parser-condition |
|---|
| 68 | trimmed-query-string (original-query construct) |
|---|
| 69 | (format nil "SELECT, PREFIX or BASE, but found: ~a..." |
|---|
| 70 | (subseq trimmed-query-string 0 10))))))))) |
|---|
| 71 | |
|---|
| 72 | |
|---|
| 73 | (defgeneric parse-select (construct query-string) |
|---|
| 74 | (:documentation "The entry-point of the parsing of the select - where |
|---|
| 75 | statement.") |
|---|
| 76 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 77 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 78 | (next-query (if (string-starts-with trimmed-str "WHERE") |
|---|
| 79 | trimmed-str |
|---|
| 80 | (parse-variables construct trimmed-str)))) |
|---|
| 81 | (unless (string-starts-with next-query "WHERE") |
|---|
| 82 | (error (make-sparql-parser-condition |
|---|
| 83 | next-query (original-query construct) "WHERE"))) |
|---|
| 84 | (let* ((triples (string-after next-query "WHERE")) |
|---|
| 85 | (query-tail (parse-where construct triples))) |
|---|
| 86 | (when (> (length query-tail) 0) |
|---|
| 87 | (error (make-sparql-parser-condition |
|---|
| 88 | query-tail (original-query construct) |
|---|
| 89 | "The end of the query. Solution sequence modifiers are not supported yet."))) |
|---|
| 90 | construct)))) |
|---|
| 91 | |
|---|
| 92 | |
|---|
| 93 | (defgeneric parse-where (construct query-string) |
|---|
| 94 | (:documentation "The entry-point for the parsing of the WHERE statement.") |
|---|
| 95 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 96 | (let ((trimmed-str (cut-comment query-string))) |
|---|
| 97 | (unless (string-starts-with trimmed-str "{") |
|---|
| 98 | (error (make-sparql-parser-condition trimmed-str |
|---|
| 99 | (original-query construct) "{"))) |
|---|
| 100 | (let ((query-tail (parse-group construct (subseq trimmed-str 1)))) |
|---|
| 101 | (when (> (length (trim-whitespace query-tail)) 0) |
|---|
| 102 | (make-sparql-parser-condition |
|---|
| 103 | query-tail (original-query construct) "end of query, solution sequences and modifiers are not supported")) |
|---|
| 104 | query-tail)))) |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | (defgeneric parse-group (construct query-string &key last-subject) |
|---|
| 108 | (:documentation "The entry-point for the parsing of a {} statement.") |
|---|
| 109 | (:method ((construct SPARQL-Query) (query-string String) |
|---|
| 110 | &key (last-subject nil)) |
|---|
| 111 | (declare (type (or Null SPARQL-Triple-Elem) last-subject)) |
|---|
| 112 | (let ((trimmed-str (cut-comment query-string))) |
|---|
| 113 | (cond ((string-starts-with trimmed-str "BASE") |
|---|
| 114 | (parse-base construct (string-after trimmed-str "BASE") |
|---|
| 115 | #'(lambda(constr query-str) |
|---|
| 116 | (parse-group constr query-str |
|---|
| 117 | :last-subject last-subject)))) |
|---|
| 118 | ((string-starts-with trimmed-str "{") |
|---|
| 119 | (error (make-sparql-parser-condition |
|---|
| 120 | trimmed-str (original-query construct) |
|---|
| 121 | "FILTER, BASE, or triple. Grouping is currently no implemented."))) |
|---|
| 122 | ((string-starts-with trimmed-str "FILTER") |
|---|
| 123 | (parse-filter construct (string-after trimmed-str "FILTER"))) |
|---|
| 124 | ((string-starts-with trimmed-str "OPTIONAL") |
|---|
| 125 | (error (make-sparql-parser-condition |
|---|
| 126 | trimmed-str (original-query construct) |
|---|
| 127 | "FILTER, BASE, or triple. Grouping is currently no implemented."))) |
|---|
| 128 | ((string-starts-with trimmed-str "UNION") |
|---|
| 129 | (error (make-sparql-parser-condition |
|---|
| 130 | trimmed-str (original-query construct) |
|---|
| 131 | "FILTER, BASE, or triple. Grouping is currently no implemented."))) |
|---|
| 132 | ((string-starts-with trimmed-str "}") ;ending of this group |
|---|
| 133 | (subseq trimmed-str 1)) |
|---|
| 134 | (t |
|---|
| 135 | (parse-triple construct trimmed-str :last-subject last-subject)))))) |
|---|
| 136 | |
|---|
| 137 | |
|---|
| 138 | (defgeneric parse-triple-elem (construct query-string &key literal-allowed) |
|---|
| 139 | (:documentation "A helper function to parse a subject or predicate of an RDF triple.") |
|---|
| 140 | (:method ((construct SPARQL-Query) (query-string String) |
|---|
| 141 | &key (literal-allowed nil)) |
|---|
| 142 | (declare (Boolean literal-allowed)) |
|---|
| 143 | (let ((trimmed-str (cut-comment query-string))) |
|---|
| 144 | (cond ((string-starts-with trimmed-str "a ") ;;rdf:type |
|---|
| 145 | (list :next-query (cut-comment (subseq trimmed-str 1)) |
|---|
| 146 | :value (make-instance 'SPARQL-Triple-Elem |
|---|
| 147 | :elem-type 'IRI |
|---|
| 148 | :value *type-psi*))) |
|---|
| 149 | ((string-starts-with trimmed-str "<") |
|---|
| 150 | (let ((result (parse-base-suffix-pair construct trimmed-str))) |
|---|
| 151 | (if (and (not (variable-p (getf result :value))) |
|---|
| 152 | (string= (value (getf result :value)) *rdf-type*)) |
|---|
| 153 | (list :next-query (getf result :next-query) |
|---|
| 154 | :value (make-instance 'SPARQL-Triple-Elem |
|---|
| 155 | :elem-type 'IRI |
|---|
| 156 | :value *type-psi*)) |
|---|
| 157 | result))) |
|---|
| 158 | ((or (string-starts-with trimmed-str "?") |
|---|
| 159 | (string-starts-with trimmed-str "$")) |
|---|
| 160 | (let ((result |
|---|
| 161 | (parse-variable-name construct trimmed-str |
|---|
| 162 | :additional-delimiters (list "}")))) |
|---|
| 163 | (list :next-query (cut-comment (getf result :next-query)) |
|---|
| 164 | :value (make-instance 'SPARQL-Triple-Elem |
|---|
| 165 | :elem-type 'VARIABLE |
|---|
| 166 | :value (getf result :value))))) |
|---|
| 167 | (t |
|---|
| 168 | (if (or (string-starts-with-digit trimmed-str) |
|---|
| 169 | (string-starts-with trimmed-str "\"") |
|---|
| 170 | (string-starts-with trimmed-str "true") |
|---|
| 171 | (string-starts-with trimmed-str "false") |
|---|
| 172 | (string-starts-with trimmed-str "'")) |
|---|
| 173 | (progn |
|---|
| 174 | (unless literal-allowed |
|---|
| 175 | (error (make-sparql-parser-condition |
|---|
| 176 | trimmed-str (original-query construct) |
|---|
| 177 | "an IRI of the form prefix:suffix or <iri> but found a literal."))) |
|---|
| 178 | (parse-literal-elem construct trimmed-str)) |
|---|
| 179 | (let ((result (parse-prefix-suffix-pair construct trimmed-str))) |
|---|
| 180 | (if (and (not (variable-p (getf result :value))) |
|---|
| 181 | (string= (value (getf result :value)) *rdf-type*)) |
|---|
| 182 | (list :next-query (getf result :next-query) |
|---|
| 183 | :value (make-instance 'SPARQL-Triple-Elem |
|---|
| 184 | :elem-type 'IRI |
|---|
| 185 | :value *type-psi*)) |
|---|
| 186 | result)))))))) |
|---|
| 187 | |
|---|
| 188 | (defgeneric parse-literal-elem (construct query-string) |
|---|
| 189 | (:documentation "A helper-function that returns a literal vaue of the form |
|---|
| 190 | (:value (:value object :literal-type string :literal-lang |
|---|
| 191 | string :type <'LITERAL>) :next-query string).") |
|---|
| 192 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 193 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 194 | (value-type-lang-query |
|---|
| 195 | (cond ((or (string-starts-with trimmed-str "\"") |
|---|
| 196 | (string-starts-with trimmed-str "'")) |
|---|
| 197 | (parse-literal-string-value construct trimmed-str)) |
|---|
| 198 | ((string-starts-with trimmed-str "true") |
|---|
| 199 | (list :value t :type *xml-boolean* |
|---|
| 200 | :next-query (subseq trimmed-str (length "true")))) |
|---|
| 201 | ((string-starts-with trimmed-str "false") |
|---|
| 202 | (list :value nil :type *xml-boolean* |
|---|
| 203 | :next-query (subseq trimmed-str (length "false")))) |
|---|
| 204 | ((string-starts-with-digit trimmed-str) |
|---|
| 205 | (parse-literal-number-value construct trimmed-str))))) |
|---|
| 206 | (list :next-query (getf value-type-lang-query :next-query) |
|---|
| 207 | :value (make-instance |
|---|
| 208 | 'SPARQL-Triple-Elem |
|---|
| 209 | :elem-type 'LITERAL |
|---|
| 210 | :value (getf value-type-lang-query :value) |
|---|
| 211 | :literal-lang (getf value-type-lang-query :lang) |
|---|
| 212 | :literal-datatype (getf value-type-lang-query :type)))))) |
|---|
| 213 | |
|---|
| 214 | |
|---|
| 215 | (defgeneric parse-literal-string-value (construct query-string) |
|---|
| 216 | (:documentation "A helper function that parses a string that is a literal. |
|---|
| 217 | The return value is of the form |
|---|
| 218 | (list :value object :type string :lang string |
|---|
| 219 | :next-query string).") |
|---|
| 220 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 221 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 222 | (result-1 (separate-literal-value construct trimmed-str)) |
|---|
| 223 | (after-literal-value (getf result-1 :next-query)) |
|---|
| 224 | (l-value (getf result-1 :literal)) |
|---|
| 225 | (result-2 (separate-literal-lang-or-type |
|---|
| 226 | construct after-literal-value)) |
|---|
| 227 | (l-type (if (getf result-2 :type) |
|---|
| 228 | (getf result-2 :type) |
|---|
| 229 | *xml-string*)) |
|---|
| 230 | (l-lang (getf result-2 :lang)) |
|---|
| 231 | (next-query (getf result-2 :next-query))) |
|---|
| 232 | (list :next-query next-query :lang l-lang :type l-type |
|---|
| 233 | :value (cast-literal l-value l-type |
|---|
| 234 | :back-as-string-when-unsupported t))))) |
|---|
| 235 | |
|---|
| 236 | |
|---|
| 237 | (defgeneric separate-literal-lang-or-type (construct query-string) |
|---|
| 238 | (:documentation "A helper function that returns (:next-query string |
|---|
| 239 | :lang string :type string). Only one of :lang and |
|---|
| 240 | :type can be set, the other element is set to nil. |
|---|
| 241 | The query string must be the string direct after |
|---|
| 242 | the closing literal bounding.") |
|---|
| 243 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 244 | (let ((delimiters-1 (list "." ";" "}" " " (string #\tab) |
|---|
| 245 | (string #\newline))) |
|---|
| 246 | (delimiters-2 (list " ." ". " ";" "}" " " (string #\tab) |
|---|
| 247 | (string #\newline) |
|---|
| 248 | (concat "." (string #\newline)) |
|---|
| 249 | (concat "." (string #\tab))))) |
|---|
| 250 | (cond ((string-starts-with query-string "@") |
|---|
| 251 | (let ((end-pos (search-first delimiters-1 |
|---|
| 252 | (subseq query-string 1)))) |
|---|
| 253 | (unless end-pos |
|---|
| 254 | (error (make-sparql-parser-condition |
|---|
| 255 | query-string (original-query construct) |
|---|
| 256 | "'.', ';', '}', ' ', '\t', or '\n'"))) |
|---|
| 257 | (list :next-query (subseq (subseq query-string 1) end-pos) |
|---|
| 258 | :lang (subseq (subseq query-string 1) 0 end-pos) |
|---|
| 259 | :type nil))) |
|---|
| 260 | ((string-starts-with query-string "^^") |
|---|
| 261 | (let ((end-pos (search-first delimiters-2 (subseq query-string 2)))) |
|---|
| 262 | (unless end-pos |
|---|
| 263 | (error (make-sparql-parser-condition |
|---|
| 264 | query-string (original-query construct) |
|---|
| 265 | "'. ', ,' .', ';', '}', ' ', '\t', or '\n'"))) |
|---|
| 266 | (let* ((type-str (subseq (subseq query-string 2) 0 end-pos)) |
|---|
| 267 | (next-query (subseq (subseq query-string 2) end-pos)) |
|---|
| 268 | (final-type (if (get-prefix construct type-str) |
|---|
| 269 | (get-prefix construct type-str) |
|---|
| 270 | type-str))) |
|---|
| 271 | (list :next-query (cut-comment next-query) |
|---|
| 272 | :type final-type :lang nil)))) |
|---|
| 273 | (t |
|---|
| 274 | (list :next-query (cut-comment query-string) :type nil :lang nil)))))) |
|---|
| 275 | |
|---|
| 276 | |
|---|
| 277 | (defgeneric separate-literal-value (construct query-string) |
|---|
| 278 | (:documentation "A helper function that returns (:next-query string |
|---|
| 279 | :literal string). The literal string contains the |
|---|
| 280 | pure literal value.") |
|---|
| 281 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 282 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 283 | (delimiter (cond ((string-starts-with trimmed-str "\"") |
|---|
| 284 | "\"") |
|---|
| 285 | ((string-starts-with trimmed-str "'''") |
|---|
| 286 | "'''") |
|---|
| 287 | ((string-starts-with trimmed-str "'") |
|---|
| 288 | "'") |
|---|
| 289 | (t |
|---|
| 290 | (error (make-sparql-parser-condition |
|---|
| 291 | trimmed-str (original-query construct) |
|---|
| 292 | "a literal starting with ', ''', or \""))))) |
|---|
| 293 | (literal-end (find-literal-end (subseq trimmed-str (length delimiter)) |
|---|
| 294 | delimiter 0))) |
|---|
| 295 | (list :next-query (subseq trimmed-str (+ literal-end (length delimiter))) |
|---|
| 296 | :literal (subseq trimmed-str (length delimiter) literal-end))))) |
|---|
| 297 | |
|---|
| 298 | |
|---|
| 299 | (defgeneric parse-literal-number-value (construct query-string) |
|---|
| 300 | (:documentation "A helper function that parses any number that is a literal. |
|---|
| 301 | The return value is of the form |
|---|
| 302 | (list :value nil :type string :next-query string.") |
|---|
| 303 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 304 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 305 | (triple-delimiters |
|---|
| 306 | (list ". " ";" " " (string #\tab) |
|---|
| 307 | (string #\newline) "}")) |
|---|
| 308 | (end-pos (search-first triple-delimiters trimmed-str))) |
|---|
| 309 | (unless end-pos |
|---|
| 310 | (error (make-sparql-parser-condition |
|---|
| 311 | trimmed-str (original-query construct) |
|---|
| 312 | "'. ', , ';' ' ', '\\t', '\\n' or '}'"))) |
|---|
| 313 | (let* ((literal-number |
|---|
| 314 | (read-from-string |
|---|
| 315 | (let ((str-value (subseq trimmed-str 0 end-pos))) |
|---|
| 316 | (if (string-ends-with str-value ".") |
|---|
| 317 | (progn (decf end-pos) |
|---|
| 318 | (subseq str-value 0 (1- (length str-value)))) |
|---|
| 319 | str-value)))) |
|---|
| 320 | (number-type |
|---|
| 321 | (if (search "." (subseq trimmed-str 0 end-pos)) |
|---|
| 322 | *xml-double* ;could also be an xml:decimal, since the double has |
|---|
| 323 | ;a bigger range it shouldn't matter |
|---|
| 324 | *xml-integer*))) |
|---|
| 325 | (unless (numberp literal-number) |
|---|
| 326 | (error (make-sparql-parser-condition |
|---|
| 327 | trimmed-str (original-query construct) |
|---|
| 328 | "a valid number of the form '1', '1.3', 1.0e6'"))) |
|---|
| 329 | (list :value literal-number :type number-type |
|---|
| 330 | :next-query (subseq trimmed-str end-pos)))))) |
|---|
| 331 | |
|---|
| 332 | |
|---|
| 333 | (defgeneric parse-base-suffix-pair (construct query-string) |
|---|
| 334 | (:documentation "A helper function that returns a list of the form |
|---|
| 335 | (list :next-query string :value (:value uri :type 'IRI)).") |
|---|
| 336 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 337 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 338 | (result (parse-closed-value trimmed-str construct)) |
|---|
| 339 | (result-uri |
|---|
| 340 | (if (or (absolute-uri-p (getf result :value)) |
|---|
| 341 | (not (base-value construct))) |
|---|
| 342 | (getf result :value) |
|---|
| 343 | (concatenate-uri (base-value construct) |
|---|
| 344 | (getf result :value)))) |
|---|
| 345 | (next-query (getf result :next-query))) |
|---|
| 346 | (list :next-query (cut-comment next-query) |
|---|
| 347 | :value (make-instance 'SPARQL-Triple-Elem |
|---|
| 348 | :elem-type 'IRI |
|---|
| 349 | :value result-uri))))) |
|---|
| 350 | |
|---|
| 351 | |
|---|
| 352 | (defgeneric parse-prefix-suffix-pair(construct query-string) |
|---|
| 353 | (:documentation "A helper function that returns a list of the form |
|---|
| 354 | (list :next-query string :value (:value uri :type 'IRI)).") |
|---|
| 355 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 356 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 357 | (delimiters (list "." ";" "}" "<" " " (string #\newline) |
|---|
| 358 | (string #\tab))) ; "#")) |
|---|
| 359 | (end-pos (search-first delimiters trimmed-str)) |
|---|
| 360 | (elem-str (when end-pos |
|---|
| 361 | (subseq trimmed-str 0 end-pos))) |
|---|
| 362 | (prefix (when elem-str |
|---|
| 363 | (string-until elem-str ":"))) |
|---|
| 364 | (suffix (when prefix |
|---|
| 365 | (string-after elem-str ":"))) |
|---|
| 366 | (full-url |
|---|
| 367 | (when (and suffix prefix) |
|---|
| 368 | (get-prefix construct (concat prefix ":" suffix))))) |
|---|
| 369 | (unless (and end-pos prefix suffix) |
|---|
| 370 | (error (make-sparql-parser-condition |
|---|
| 371 | trimmed-str (original-query construct) |
|---|
| 372 | "An IRI of the form prefix:suffix"))) |
|---|
| 373 | (unless full-url |
|---|
| 374 | (error (make-condition |
|---|
| 375 | 'sparql-parser-error |
|---|
| 376 | :message (format nil "The prefix in \"~a:~a\" is not registered" |
|---|
| 377 | prefix suffix)))) |
|---|
| 378 | (list :next-query (cut-comment |
|---|
| 379 | (string-after trimmed-str |
|---|
| 380 | (concat prefix ":" suffix))) |
|---|
| 381 | :value (make-instance 'SPARQL-Triple-Elem |
|---|
| 382 | :elem-type 'IRI |
|---|
| 383 | :value full-url))))) |
|---|
| 384 | |
|---|
| 385 | |
|---|
| 386 | (defgeneric parse-triple (construct query-string &key last-subject) |
|---|
| 387 | (:documentation "Parses a triple within a trippel group.") |
|---|
| 388 | (:method ((construct SPARQL-Query) (query-string String) &key (last-subject nil)) |
|---|
| 389 | (declare (type (or Null SPARQL-Triple-Elem) last-subject)) |
|---|
| 390 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 391 | (subject-result (if last-subject ;;is used after a ";" |
|---|
| 392 | last-subject |
|---|
| 393 | (parse-triple-elem construct trimmed-str))) |
|---|
| 394 | (predicate-result (parse-triple-elem |
|---|
| 395 | construct |
|---|
| 396 | (if last-subject |
|---|
| 397 | trimmed-str |
|---|
| 398 | (getf subject-result :next-query)))) |
|---|
| 399 | (object-result (parse-triple-elem construct |
|---|
| 400 | (getf predicate-result :next-query) |
|---|
| 401 | :literal-allowed t))) |
|---|
| 402 | (add-triple construct |
|---|
| 403 | (make-instance 'SPARQL-Triple |
|---|
| 404 | :subject (if last-subject |
|---|
| 405 | last-subject |
|---|
| 406 | (getf subject-result :value)) |
|---|
| 407 | :predicate (getf predicate-result :value) |
|---|
| 408 | :object (getf object-result :value))) |
|---|
| 409 | (let ((tr-str (cut-comment (getf object-result :next-query)))) |
|---|
| 410 | (cond ((string-starts-with tr-str ";") |
|---|
| 411 | (parse-group construct (subseq tr-str 1) |
|---|
| 412 | :last-subject (getf subject-result :value))) |
|---|
| 413 | ((string-starts-with tr-str ".") |
|---|
| 414 | (parse-group construct (subseq tr-str 1))) |
|---|
| 415 | ((string-starts-with tr-str "}") |
|---|
| 416 | (parse-group construct tr-str))))))) |
|---|
| 417 | |
|---|
| 418 | |
|---|
| 419 | (defgeneric parse-variables (construct query-string) |
|---|
| 420 | (:documentation "Parses the variables of the SELECT statement |
|---|
| 421 | and adds them to the passed construct.") |
|---|
| 422 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 423 | (let ((trimmed-str (cut-comment query-string))) |
|---|
| 424 | (if (string-starts-with trimmed-str "WHERE") |
|---|
| 425 | trimmed-str |
|---|
| 426 | (if (string-starts-with trimmed-str "*") |
|---|
| 427 | (progn (add-variable construct "*") |
|---|
| 428 | (parse-variables construct (string-after trimmed-str "*"))) |
|---|
| 429 | (let ((result (parse-variable-name construct trimmed-str))) |
|---|
| 430 | (add-variable construct (trim-whitespace-right (getf result :value))) |
|---|
| 431 | (parse-variables construct (getf result :next-query)))))))) |
|---|
| 432 | |
|---|
| 433 | |
|---|
| 434 | (defgeneric parse-variable-name (construct query-string &key additional-delimiters) |
|---|
| 435 | (:documentation "A helper function that parses the first non-whitespace character |
|---|
| 436 | in the query. since it must be a variable, it must be prefixed |
|---|
| 437 | by a ? or $. The return value is of the form |
|---|
| 438 | (:next-query string :value string).") |
|---|
| 439 | (:method ((construct SPARQL-Query) (query-string String) |
|---|
| 440 | &key (additional-delimiters)) |
|---|
| 441 | (declare (List additional-delimiters)) |
|---|
| 442 | (let ((trimmed-str (cut-comment query-string)) |
|---|
| 443 | (delimiters (append |
|---|
| 444 | (list " " "?" "$" "." (string #\newline) (string #\tab)) |
|---|
| 445 | additional-delimiters))) |
|---|
| 446 | (unless (or (string-starts-with trimmed-str "?") |
|---|
| 447 | (string-starts-with trimmed-str "$")) |
|---|
| 448 | (error (make-sparql-parser-condition |
|---|
| 449 | trimmed-str (original-query construct) "? or $"))) |
|---|
| 450 | (let* ((var-name-end (search-first delimiters (subseq trimmed-str 1))) |
|---|
| 451 | (var-name |
|---|
| 452 | (if var-name-end |
|---|
| 453 | (subseq trimmed-str 0 (+ 1 var-name-end)) |
|---|
| 454 | (error (make-sparql-parser-condition |
|---|
| 455 | trimmed-str (original-query construct) |
|---|
| 456 | "space, newline, tab, ?, ., $ or WHERE")))) |
|---|
| 457 | (next-query (string-after trimmed-str var-name)) |
|---|
| 458 | (normalized-var-name |
|---|
| 459 | (if (<= (length var-name) 1) |
|---|
| 460 | (error (make-sparql-parser-condition |
|---|
| 461 | next-query (original-query construct) |
|---|
| 462 | "a variable name")) |
|---|
| 463 | (subseq var-name 1)))) |
|---|
| 464 | (list :next-query next-query :value normalized-var-name))))) |
|---|
| 465 | |
|---|
| 466 | |
|---|
| 467 | (defgeneric parse-base (construct query-string next-fun) |
|---|
| 468 | (:documentation "Parses the Base statment and sets the corresponding |
|---|
| 469 | attribute in the query-construct. Since the BASE statement |
|---|
| 470 | may appear in different states the next-fun defines the next |
|---|
| 471 | call function that calls the next transitions and states.") |
|---|
| 472 | (:method ((construct SPARQL-Query) (query-string String) (next-fun Function)) |
|---|
| 473 | (let* ((trimmed-str (cut-comment query-string)) |
|---|
| 474 | (result (parse-closed-value trimmed-str construct))) |
|---|
| 475 | (setf (base-value construct) (getf result :value)) |
|---|
| 476 | (funcall next-fun construct (getf result :next-query))))) |
|---|
| 477 | |
|---|
| 478 | |
|---|
| 479 | (defgeneric parse-prefixes (construct query-string) |
|---|
| 480 | (:documentation "Sets the correponding prefix-tuples in the passed object.") |
|---|
| 481 | (:method ((construct SPARQL-Query) (query-string String)) |
|---|
| 482 | (let ((trimmed-string (cut-comment query-string))) |
|---|
| 483 | (if (string-starts-with trimmed-string ":") |
|---|
| 484 | (let ((results |
|---|
| 485 | (parse-closed-value (subseq trimmed-string 1) construct))) |
|---|
| 486 | (add-prefix construct *empty-label* (getf results :value)) |
|---|
| 487 | (parser-start construct (getf results :next-query))) |
|---|
| 488 | (let* ((label-name |
|---|
| 489 | (trim-whitespace-right (string-until trimmed-string ":"))) |
|---|
| 490 | (next-query-str |
|---|
| 491 | (trim-whitespace-left (string-after trimmed-string ":"))) |
|---|
| 492 | (results (parse-closed-value next-query-str construct))) |
|---|
| 493 | (when (string= label-name trimmed-string) |
|---|
| 494 | (error (make-sparql-parser-condition |
|---|
| 495 | trimmed-string (original-query construct) ":"))) |
|---|
| 496 | (add-prefix construct label-name (getf results :value)) |
|---|
| 497 | (parser-start construct (getf results :next-query))))))) |
|---|