;;;; Copyright (C) 2020 Julien Lepiller ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 3 of the License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ;;;; (define-module (turtle tordf) #:use-module (ice-9 match) #:use-module (ice-9 textual-ports) #:use-module (iri iri) #:use-module (turtle parser) #:use-module (srfi srfi-9) #:use-module (rdf rdf) #:export (turtle->rdf)) (define-record-type parser-state (make-parser-state base-uri namespaces bnode-labels cur-subject cur-predicate blank-node-gen result) parser-state? (base-uri parser-state-base-uri) (namespaces parser-state-namespaces) (bnode-labels parser-state-bnode-labels) (cur-subject parser-state-cur-subject) (cur-predicate parser-state-cur-predicate) (blank-node-gen parser-state-blank-node-gen) (result parser-state-result)) (define* (update-parser-state state #:key (base-uri (parser-state-base-uri state)) (namespaces (parser-state-namespaces state)) (bnode-labels (parser-state-bnode-labels state)) (cur-subject (parser-state-cur-subject state)) (cur-predicate (parser-state-cur-predicate state)) (blank-node-gen (parser-state-blank-node-gen state)) (result (parser-state-result state))) (make-parser-state base-uri namespaces bnode-labels cur-subject cur-predicate blank-node-gen result)) (define (create-generate-blank-node) (define num 0) (lambda () (set! num (+ num 1)) num)) (define (add-ns-to-state state ns iri) (pk 'iri iri) (update-parser-state state #:namespaces (cons (cons ns iri) (parser-state-namespaces state)))) (define (parse-iri iri state) (format #t "iri: ~a~%" iri) (match iri (('iri ('prefixed-name ('pname-ln ('pname-ns ns) suffix))) `(("iri" . ,(string-append (assoc-ref (parser-state-namespaces state) ns) suffix)) ("state" . ,state))) (('iri ('prefixed-name ('pname-ln ('pname-ns suffix)))) `(("iri" . ,(string-append (assoc-ref (parser-state-namespaces state) "") suffix)) ("state" . ,state))) (('iri ('prefixed-name 'pname-ns)) `(("iri" . ,(assoc-ref (parser-state-namespaces state) "")) ("state" . ,state))) (('iri 'iriref) `(("iri" . ,(resolve-iri (parser-state-base-uri state) "")) ("state" . ,state))) (('iri ('iriref iri)) `(("iri" . ,(resolve-iri (parser-state-base-uri state) iri)) ("state" . ,state))) (('blank-node ('blank-node-label label)) (if (assoc-ref (parser-state-bnode-labels state) label) `(("iri" . ,(assoc-ref (parser-state-bnode-labels state) label)) ("state" . ,state)) (let ((node ((parser-state-blank-node-gen state)))) `(("iri" . ,node) ("state" . ,(update-parser-state state #:bnode-labels (cons (cons label node) (parser-state-bnode-labels state)))))))))) (define (parse-verb verb state) (match verb ("a" `(("verb" . "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") ("state" . ,state))) (('predicate iri) (let ((res (parse-iri iri state))) `(("verb" . ,(assoc-ref res "iri")) ("state" . ,(assoc-ref res "state"))))))) (define (parse-object object state) (pk 'object object) (match object (('rdf-literal ('string-pat (_ str))) (update-parser-state state #:result (cons (make-rdf-triple (parser-state-cur-subject state) (parser-state-cur-predicate state) (make-rdf-literal str "http://www.w3.org/2001/XMLSchema#string" #f)) (parser-state-result state)))) (('rdf-literal ('string-pat (_ str)) ('langtag lang)) (update-parser-state state #:result (cons (make-rdf-triple (parser-state-cur-subject state) (parser-state-cur-predicate state) (make-rdf-literal str "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString" lang)) (parser-state-result state)))) (('numeric-literal ('integer int)) (update-parser-state state #:result (cons (make-rdf-triple (parser-state-cur-subject state) (parser-state-cur-predicate state) (make-rdf-literal int "http://www.w3.org/2001/XMLSchema#integer" #f)) (parser-state-result state)))) (('boolean-literal bool) (update-parser-state state #:result (cons (make-rdf-triple (parser-state-cur-subject state) (parser-state-cur-predicate state) (make-rdf-literal bool "http://www.w3.org/2001/XMLSchema#boolean" #f)) (parser-state-result state)))) (('blank-node-property-list ('predicate-object-list po ...)) (let* ((node ((parser-state-blank-node-gen state))) (new-state (parse-predicate-object po (update-parser-state state #:cur-subject node)))) (update-parser-state new-state #:cur-subject (parser-state-cur-subject state) #:cur-predicate (parser-state-cur-predicate state) #:result (cons (make-rdf-triple (parser-state-cur-subject state) (parser-state-cur-predicate state) node) (parser-state-result new-state))))) (('collection objects ...) (let loop ((objects objects) (state state)) (match objects ('() state) ((('object object) objects ...) (loop objects (parse-object object state)))))) (('iri _) (let* ((res (parse-iri object state)) (iri (assoc-ref res "iri")) (state (assoc-ref res "state"))) (update-parser-state state #:result (cons (make-rdf-triple (parser-state-cur-subject state) (parser-state-cur-predicate state) iri) (parser-state-result state))))))) (define (parse-object-list ol state) (let loop ((ol ol) (state state)) (pk 'ol ol) (match ol ('() state) ((('object object) ol ...) (loop ol (parse-object object state))) ((ol) (loop ol state))))) (define (parse-predicate-object po state) (let loop ((po po) (state state)) (pk 'po po) (match po ((('verb verb) ('object-list ol ...) po) (let* ((verb (parse-verb verb state)) (state (assoc-ref verb "state")) (verb (assoc-ref verb "verb")) (new-state (update-parser-state state #:cur-predicate verb)) (res (parse-object-list ol new-state))) (loop po res))) ((('verb verb) ('object-list ol ...)) (let* ((verb (parse-verb verb state)) (state (assoc-ref verb "state")) (verb (assoc-ref verb "verb")) (new-state (update-parser-state state #:cur-predicate verb)) (res (parse-object-list ol new-state))) res)) (((('verb verb) ('object-list ol ...)) po ...) (let* ((verb (parse-verb verb state)) (state (assoc-ref verb "state")) (verb (assoc-ref verb "verb")) (new-state (update-parser-state state #:cur-predicate verb)) (res (parse-object-list ol new-state))) (loop po res))) ('() state) ((po) (loop po state))))) (define (parse-triples t state) (match t ((('subject iri) ('predicate-object-list predicate-object ...)) (let* ((res (parse-iri iri state)) (iri (assoc-ref res "iri")) (state (assoc-ref res "state")) (state (update-parser-state state #:cur-subject iri))) (parse-predicate-object predicate-object state))))) (define (parse-turtle-doc parse-tree state) (let loop ((parse-tree parse-tree) (state state)) (match parse-tree ('() (parser-state-result state)) ((('prefix-id ('pname-ns ns) ('iriref iri)) parse-tree ...) (loop parse-tree (add-ns-to-state state ns (resolve-iri (parser-state-base-uri state) iri)))) ((('prefix-id ('pname-ns ('iriref iri))) parse-tree ...) (loop parse-tree (add-ns-to-state state "" (resolve-iri (parser-state-base-uri state) iri)))) ((('sparql-prefix ('pname-ns ns) ('iriref iri)) parse-tree ...) (loop parse-tree (add-ns-to-state state ns (resolve-iri (parser-state-base-uri state) iri)))) ((('sparql-prefix ('pname-ns ('iriref iri))) parse-tree ...) (loop parse-tree (add-ns-to-state state "" (resolve-iri (parser-state-base-uri state) iri)))) ((('base ('iriref iri)) parse-tree ...) (loop parse-tree (update-parser-state state #:base-uri (resolve-iri (parser-state-base-uri state) iri)))) ((('sparql-base ('iriref iri)) parse-tree ...) (loop parse-tree (update-parser-state state #:base-uri (resolve-iri iri (parser-state-base-uri state))))) ((('triples t ...) parse-tree ...) (format #t "triples: ~a~%" t) (let ((res (parse-triples t state))) (loop parse-tree (parse-triples t state)))) ;; otherwise, it's a single element, not a list of statements (((? symbol? _) _ ...) (loop (list parse-tree) state))))) (define (tordf parse-tree base) (define state (make-parser-state base '() '() #f #f (create-generate-blank-node) '())) (parse-turtle-doc parse-tree state)) (define (turtle->rdf str-or-file base) (define str (cond ((file-exists? str-or-file) (call-with-input-file str-or-file get-string-all)) ((string? str-or-file) str-or-file))) (let ((parse-tree (parse-turtle str))) (tordf parse-tree base)))