generate-vocabulary.scm
1 | ;;;; Copyright (C) 2020 Julien Lepiller <julien@lepiller.eu> |
2 | ;;;; |
3 | ;;;; This library is free software; you can redistribute it and/or |
4 | ;;;; modify it under the terms of the GNU Lesser General Public |
5 | ;;;; License as published by the Free Software Foundation; either |
6 | ;;;; version 3 of the License, or (at your option) any later version. |
7 | ;;;; |
8 | ;;;; This library is distributed in the hope that it will be useful, |
9 | ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
11 | ;;;; Lesser General Public License for more details. |
12 | ;;;; |
13 | ;;;; You should have received a copy of the GNU Lesser General Public |
14 | ;;;; License along with this library; if not, write to the Free Software |
15 | ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
16 | |
17 | (define-module (schema.org generate-vocabulary) |
18 | #:use-module (ice-9 match) |
19 | #:use-module (json) |
20 | #:use-module (jsonld) |
21 | #:export (generate-schema)) |
22 | |
23 | (define (generate-schema filename description-file) |
24 | (define input (if (equal? description-file "") |
25 | "https://schema.org/version/8.0/schema.jsonld" |
26 | (call-with-input-file description-file json->scm))) |
27 | (define definitions (assoc-ref (car (array->list (expand input))) "@graph")) |
28 | |
29 | (with-output-to-file filename |
30 | (lambda _ |
31 | (format #t ";; Module generated by (schema.org generate-vocabulary)~%") |
32 | (format #t ";; from the schema.org jsonld description.") |
33 | (format #t "(define-module (schema.org ~a)~%" (basename filename ".scm")) |
34 | (format #t " #:use-module (activitystreams predicates)~%") |
35 | (format #t " #:use-module (activitystreams ontology)~%") |
36 | (format #t " #:export (schema.org-ontology)~%~%") |
37 | (for-each |
38 | (lambda (definition) |
39 | (let ((types (array->list (assoc-ref definition "@type")))) |
40 | (if (member "http://www.w3.org/2000/01/rdf-schema#Class" types) |
41 | (unless (datatype? definition) |
42 | (generate-class definition)) |
43 | (generate-property definition)))) |
44 | (array->list definitions)) |
45 | (let* ((types (filter |
46 | (lambda (def) |
47 | (member "http://www.w3.org/2000/01/rdf-schema#Class" |
48 | (array->list (assoc-ref def "@type")))) |
49 | (array->list definitions))) |
50 | (types (map |
51 | (lambda (def) |
52 | (assoc-ref def "http://www.w3.org/2000/01/rdf-schema#label")) |
53 | types)) |
54 | (types (map |
55 | (lambda (def) |
56 | (assoc-ref (car (array->list def)) "@value")) |
57 | types)) |
58 | (predicates (filter |
59 | (lambda (def) |
60 | (and |
61 | (not (datatype? def)) |
62 | (not |
63 | (member "http://www.w3.org/2000/01/rdf-schema#Class" |
64 | (array->list (assoc-ref def "@type")))))) |
65 | (array->list definitions))) |
66 | (predicates (map |
67 | (lambda (def) |
68 | (assoc-ref def "http://www.w3.org/2000/01/rdf-schema#label")) |
69 | predicates)) |
70 | (predicates (map |
71 | (lambda (def) |
72 | (assoc-ref (car (array->list def)) "@value")) |
73 | predicates))) |
74 | (format #t "(define schema.org-ontology~%") |
75 | (format #t " (make-ontology~%") |
76 | (format #t " '(\"http://schema.org/\")~%") |
77 | (format #t " (list ~a)~%" (cut-str-list types 80)) |
78 | (format #t " (list ~a)))~%" (cut-str-list predicates 80)))))) |
79 | |
80 | (define (cut-str str n) |
81 | "Cut a string @var{str} at @var{n} characters by placing a @code{\\n}, so that |
82 | the string is aligned to @var{n} characters." |
83 | (let loop ((str str)) |
84 | (if (< (string-length str) (+ n 1)) |
85 | str |
86 | (string-append |
87 | (substring str 0 n) |
88 | "\n" |
89 | (loop (substring str n)))))) |
90 | |
91 | (define (cut-str-list lst n) |
92 | (let loop ((lst lst) (result '(()))) |
93 | (match lst |
94 | (() (string-join |
95 | (reverse (map (lambda (l) (string-join l " ")) (map reverse result))) |
96 | "\n")) |
97 | ((word lst ...) |
98 | (let ((current-line (car result))) |
99 | (if (> (string-length (string-join (cons word current-line) " ")) n) |
100 | (loop lst (cons (list word) result)) |
101 | (loop lst (cons (cons word current-line) (cdr result))))))))) |
102 | |
103 | |
104 | (define (generate-class definition) |
105 | (let* ((id (assoc-ref definition "@id")) |
106 | (comment (assoc-ref definition "http://www.w3.org/2000/01/rdf-schema#comment")) |
107 | (comment (assoc-ref (car (array->list comment)) "@value")) |
108 | (comment (cut-str comment 76)) |
109 | (comment (string-join (string-split comment #\") "\\\"")) |
110 | (label (assoc-ref definition "http://www.w3.org/2000/01/rdf-schema#label")) |
111 | (label (assoc-ref (car (array->list label)) "@value")) |
112 | (subclass-of (assoc-ref definition "http://www.w3.org/2000/01/rdf-schema#subClassOf")) |
113 | (subclass-of (if subclass-of (array->list subclass-of) '())) |
114 | (subclass-of (map (lambda (c) (basename (assoc-ref c "@id"))) |
115 | subclass-of))) |
116 | (format #t "(define-public ~a~%" label) |
117 | (format #t " (build-as-type \"~a\"~% #:uri \"~a\"~% #:comment~% \"~a\"" |
118 | label id comment) |
119 | (unless (null? subclass-of) |
120 | (format #t "~% #:subclass-of (list ~a)" (string-join subclass-of " "))) |
121 | (format #t "))~%~%"))) |
122 | |
123 | |
124 | (define (generate-property definition) |
125 | (let* ((id (assoc-ref definition "@id")) |
126 | (comment (assoc-ref definition "http://www.w3.org/2000/01/rdf-schema#comment")) |
127 | (comment (assoc-ref (car (array->list comment)) "@value")) |
128 | (comment (cut-str comment 76)) |
129 | (comment (string-join (string-split comment #\") "\\\"")) |
130 | (label (assoc-ref definition "http://www.w3.org/2000/01/rdf-schema#label")) |
131 | (label (assoc-ref (car (array->list label)) "@value")) |
132 | (domain (assoc-ref definition "http://schema.org/domainIncludes")) |
133 | (domain (if domain (array->list domain) '())) |
134 | (domain (map (lambda (c) (basename (assoc-ref c "@id"))) domain)) |
135 | (domain (map (lambda (c) (or (assoc-ref datatypes c) c)) domain)) |
136 | (range (assoc-ref definition "http://schema.org/rangeIncludes")) |
137 | (range (if range (array->list range) '())) |
138 | (range (map (lambda (c) (basename (assoc-ref c "@id"))) range)) |
139 | (range (map (lambda (c) (or (assoc-ref datatypes c) c)) range)) |
140 | (subproperty-of (assoc-ref definition "http://www.w3.org/2000/01/rdf-schema#subPropertyOf")) |
141 | (subproperty-of (if subproperty-of (array->list subproperty-of) '())) |
142 | (subproperty-of (map (lambda (c) (basename (assoc-ref c "@id"))) |
143 | subproperty-of))) |
144 | (format #t "(define-public ~a~%" label) |
145 | (format #t " (build-as-property~%") |
146 | (format #t " \"~a\" (list ~a) (list ~a)~%" label (string-join domain " ") |
147 | (string-join range " ")) |
148 | (format #t " #:uri \"~a\"~%" id) |
149 | (format #t " #:comment~% \"~a\"" comment) |
150 | (unless (null? subproperty-of) |
151 | (format #t "~% #:subproperty-of (list ~a)" (string-join subproperty-of " "))) |
152 | (format #t "))~%~%"))) |
153 | |
154 | (define datatypes |
155 | `(("False" . "(lambda (t) (equal? t #f))") |
156 | ("True" . "(lambda (t) (equal? t #t))") |
157 | ("DataType" . "procedure?") |
158 | ("Boolean" . "boolean?") |
159 | ("Date" . "date?") |
160 | ("DateTime" . "date-time?") |
161 | ("Number" . "number?") |
162 | ("Float" . "number?") |
163 | ("Integer" . "integer?") |
164 | ("Text" . "string-or-lang-string?") |
165 | ("CssSelectorType" . "string?") |
166 | ("PronounceableText" . "string-or-lang-string?") |
167 | ("URL" . "uri?") |
168 | ("XPathType" . "string?") |
169 | ("Time" . "time?"))) |
170 | |
171 | (define (datatype? definition) |
172 | (let* ((labels (array->list (assoc-ref definition "http://www.w3.org/2000/01/rdf-schema#label"))) |
173 | (label (assoc-ref (car labels) "@value"))) |
174 | (member label (map car datatypes)))) |
175 |