compaction.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 | |
18 | (define-module (jsonld compaction) |
19 | #:use-module (jsonld context) |
20 | #:use-module (jsonld context-processing) |
21 | #:use-module (jsonld inverse-context-creation) |
22 | #:use-module (jsonld iri-compaction) |
23 | #:use-module (jsonld iri-expansion) |
24 | #:use-module (jsonld json) |
25 | #:use-module (jsonld value-compaction) |
26 | #:export (compaction)) |
27 | |
28 | (define-syntax update-result |
29 | (syntax-rules () |
30 | ((_ result nest-term nest-result) |
31 | (set! result |
32 | (if nest-term |
33 | (alist-set result nest-term nest-result) |
34 | nest-result))))) |
35 | |
36 | (define (add-value object key value as-array?) |
37 | (let* ((value (if (json-array? value) |
38 | (array->list value) |
39 | (list value))) |
40 | (original (if (json-has-key? object key) |
41 | (assoc-ref object key) |
42 | #())) |
43 | (original (if (json-array? original) |
44 | (array->list original) |
45 | (list original))) |
46 | (new-value (append original value)) |
47 | (new-value |
48 | (if (and (= (length new-value) 1) (not as-array?)) |
49 | (car new-value) |
50 | (list->array 1 new-value)))) |
51 | (alist-set object key new-value))) |
52 | |
53 | (define* (compaction active-context inverse-context active-property element |
54 | #:key (compact-arrays? #f) (ordered? #f) processing-mode) |
55 | (pk 'compaction element) |
56 | ;; 1 |
57 | (let ((type-scoped-context active-context) |
58 | (def (term-definition-ref active-context active-property))) |
59 | (cond |
60 | ;; 2 |
61 | ((scalar? element) |
62 | element) |
63 | ;; 3 |
64 | ((json-array? element) |
65 | ;; 3.1 |
66 | (let ((result '())) |
67 | ;; 3.2 |
68 | (for-each |
69 | (lambda (item) |
70 | ;; 3.2.1 |
71 | (let ((compacted-item (compaction active-context inverse-context |
72 | active-property item |
73 | #:compact-arrays? compact-arrays? |
74 | #:ordered? ordered? |
75 | #:processing-mode processing-mode))) |
76 | ;; 3.2.2 |
77 | (unless (json-null? compacted-item) |
78 | (set! result (cons compacted-item result))))) |
79 | (array->list element)) |
80 | (set! result (reverse result)) |
81 | (if (let ((container-mapping |
82 | (container-mapping active-context active-property))) |
83 | (or (not (= (length result) 1)) |
84 | (equal? compact-arrays? #f) |
85 | (equal? active-property "@graph") |
86 | (equal? active-property "@set") |
87 | (member "@set" container-mapping) |
88 | (member "@list" container-mapping))) |
89 | ;; 3.3 |
90 | (list->array 1 result) |
91 | ;; 3.4 |
92 | (car result)))) |
93 | ;; 4 |
94 | (else |
95 | ;; 5 |
96 | (when (not-null-or-false (active-context-previous active-context)) |
97 | (unless (or (json-has-key? element "@value") |
98 | (and (json-has-key? element "@id") |
99 | (null? (filter (lambda (kp) (not (equal? (car kp) "@id"))) |
100 | element)))) |
101 | (set! active-context (active-context-previous active-context)) |
102 | (set! inverse-context (inverse-context-creation active-context)))) |
103 | ;; 6 |
104 | (when (and (term-definition? def) |
105 | (not-null-or-false (term-definition-context def))) |
106 | ;; 6.1 |
107 | (set! active-context |
108 | (context-processing active-context (term-definition-context def) |
109 | (term-definition-base-url def) |
110 | #:override-protected? #t)) |
111 | ;; 6.2 |
112 | (set! inverse-context (inverse-context-creation active-context))) |
113 | ;; 7 |
114 | (cond |
115 | ((and (or (json-has-key? element "@value") |
116 | (json-has-key? element "@id")) |
117 | (let ((compact |
118 | (value-compaction active-context inverse-context |
119 | active-property element processing-mode))) |
120 | (or (scalar? compact) |
121 | (and (term-definition? def) |
122 | (equal? (term-definition-type def) "@json"))))) |
123 | (value-compaction active-context inverse-context active-property |
124 | element processing-mode)) |
125 | ;; 8 |
126 | ((and (json-has-key? element "@list") |
127 | (member "@list" (container-mapping active-context active-property))) |
128 | (compaction active-context inverse-context active-property |
129 | (assoc-ref element "@list") |
130 | #:compact-arrays? compact-arrays? |
131 | #:ordered? ordered?)) |
132 | ;; 9 and 10 |
133 | (else |
134 | (let ((inside-reverse? (equal? active-property "@reverse")) |
135 | (result '())) |
136 | ;; 11 |
137 | (when (json-has-key? element "@type") |
138 | (let* ((types (assoc-ref element "@type")) |
139 | (types (if (json-array? types) |
140 | (array->list types) |
141 | (list types))) |
142 | (compacted-types |
143 | (map |
144 | (lambda (type) |
145 | (iri-compaction active-context inverse-context type |
146 | #:vocab? #t #:processing-mode processing-mode)) |
147 | types))) |
148 | (pk 'compacted-types compacted-types) |
149 | (for-each |
150 | (lambda (term) |
151 | ;; 11.1 |
152 | (when (and |
153 | (term-definition? (term-definition-ref type-scoped-context term)) |
154 | (term-definition-context |
155 | (term-definition-ref type-scoped-context term))) |
156 | ;; 11.1.1 |
157 | (set! active-context |
158 | (context-processing |
159 | active-context |
160 | (term-definition-context |
161 | (term-definition-ref type-scoped-context term)) |
162 | (term-definition-base-url |
163 | (term-definition-ref type-scoped-context term)) |
164 | #:propagate? #f)) |
165 | ;; 11.1.2 |
166 | (set! inverse-context |
167 | (inverse-context-creation active-context)))) |
168 | (sort compacted-types string<=?)))) |
169 | ;; 12 |
170 | (for-each-pair |
171 | (lambda (expanded-property expanded-value) |
172 | (set! result |
173 | (step-12 active-context inverse-context expanded-property |
174 | expanded-value result compact-arrays? ordered? |
175 | inside-reverse? type-scoped-context active-property |
176 | processing-mode))) |
177 | (if ordered? |
178 | (alist-sort-by-key element) |
179 | element)) |
180 | ;; 13 |
181 | result))))))) |
182 | |
183 | (define (step-12 active-context inverse-context expanded-property expanded-value |
184 | result compact-arrays? ordered? inside-reverse? type-scoped-context |
185 | active-property processing-mode) |
186 | (cond |
187 | ;; 12.1 |
188 | ((equal? expanded-property "@id") |
189 | (pk 'expanded-value expanded-value) |
190 | ;; XXX: not clear what to do if expanded-value is not a |
191 | ;; string, make sure there is a test |
192 | (let ((compacted-value |
193 | (if (string? expanded-value) |
194 | (iri-compaction active-context inverse-context expanded-value |
195 | #:vocab? #f #:processing-mode processing-mode) |
196 | expanded-value)) |
197 | (alias (iri-compaction active-context inverse-context expanded-property |
198 | #:vocab? #t #:processing-mode processing-mode))) |
199 | (pk 'compacted-value compacted-value) |
200 | (pk 'alias alias) |
201 | (set! result |
202 | (alist-set result alias compacted-value)))) |
203 | ;; 12.2 |
204 | ((equal? expanded-property "@type") |
205 | (let* ((compacted-value #f) |
206 | (alias (iri-compaction active-context inverse-context expanded-property |
207 | #:vocab? #t #:processing-mode processing-mode)) |
208 | (as-array? |
209 | (or |
210 | (and |
211 | (not (processing-mode-1.0? processing-mode)) |
212 | (member "@set" (container-mapping active-context alias))) |
213 | (not compact-arrays?))) |
214 | (type-scoped-inverse-context |
215 | (inverse-context-creation type-scoped-context))) |
216 | (pk 'alias alias) |
217 | (if (string? expanded-value) |
218 | (set! compacted-value |
219 | (iri-compaction type-scoped-context type-scoped-inverse-context |
220 | expanded-value |
221 | #:vocab? #t #:processing-mode processing-mode)) |
222 | (begin |
223 | (set! expanded-value (array->list expanded-value)) |
224 | (set! compacted-value |
225 | (list->array 1 |
226 | (map |
227 | (lambda (v) |
228 | (pk 'v (iri-compaction type-scoped-context type-scoped-inverse-context |
229 | v |
230 | #:vocab? #t #:processing-mode processing-mode))) |
231 | expanded-value))))) |
232 | (pk 'compacted-value compacted-value) |
233 | (when (and (json-array? compacted-value) (= (array-length compacted-value) 1)) |
234 | (set! compacted-value (car (array->list compacted-value)))) |
235 | (set! result |
236 | (add-value result alias compacted-value as-array?)))) |
237 | ;; 12.3 |
238 | ((equal? expanded-property "@reverse") |
239 | (let ((compacted-value |
240 | (compaction active-context inverse-context "@reverse" expanded-value |
241 | #:compact-arrays? compact-arrays? #:ordered? ordered?))) |
242 | ;; 12.3.2 |
243 | (for-each-pair |
244 | (lambda (property value) |
245 | (let ((def (term-definition-ref active-context property))) |
246 | (when (and (term-definition? def) (term-definition-reverse? def)) |
247 | (let ((as-array? (or |
248 | (member "@set" |
249 | (container-mapping active-context property)) |
250 | (not compact-arrays?)))) |
251 | (set! result |
252 | (add-value result property value as-array?))) |
253 | (set! compacted-value |
254 | (alist-remove compacted-value property))))) |
255 | compacted-value) |
256 | ;; 12.3.3 |
257 | (unless (null? compacted-value) |
258 | (let ((alias (iri-compaction active-context inverse-context "@reverse" |
259 | #:vocab? #t #:processing-mode processing-mode))) |
260 | (pk 'alias alias) |
261 | (set! result (alist-set result alias compacted-value)))))) |
262 | ;; 12.4 |
263 | ((equal? expanded-property "@preserve") |
264 | (let ((compacted-value |
265 | (compaction active-context inverse-context active-property |
266 | expanded-value #:compact-arrays? compact-arrays? |
267 | #:ordered? ordered?))) |
268 | (unless (equal? expanded-value #()) |
269 | (set! result (alist-set result "@preserve" compacted-value))))) |
270 | ;; 12.5 |
271 | ((and (equal? expanded-property "@index") |
272 | (member "@index" |
273 | (container-mapping active-context active-property))) |
274 | #t) |
275 | ;; 12.6 |
276 | ((or (equal? expanded-property "@direction") |
277 | (equal? expanded-property "@index") |
278 | (equal? expanded-property "@language") |
279 | (equal? expanded-property "@value")) |
280 | (let ((alias (iri-compaction active-context inverse-context expanded-property |
281 | #:vocab? #t #:processing-mode processing-mode))) |
282 | (pk 'alias alias) |
283 | (set! result |
284 | (alist-set result alias expanded-value)))) |
285 | (else |
286 | ;; 12.7 |
287 | (when (equal? expanded-value #()) |
288 | (let* ((item-active-property |
289 | (iri-compaction active-context inverse-context expanded-property |
290 | #:value expanded-value |
291 | #:vocab? #t |
292 | #:reverse? inside-reverse? |
293 | #:processing-mode processing-mode)) |
294 | (def (term-definition-ref active-context |
295 | item-active-property)) |
296 | (nest-term (if (term-definition? def) (term-definition-nest def) #f)) |
297 | ;; 12.7.4 |
298 | (nest-result result)) |
299 | (pk 'item-active-property item-active-property) |
300 | ;; 12.7.2 |
301 | (when nest-term |
302 | (unless (or (equal? nest-term "@nest") |
303 | (equal? (expand-key active-context nest-term) |
304 | "@nest")) |
305 | (throw 'invalid-@nest-value)) |
306 | (set! nest-result |
307 | (if (json-has-key? result nest-term) |
308 | (assoc-ref result nest-term) |
309 | '()))) |
310 | ;; 12.7.4 |
311 | (set! nest-result |
312 | (add-value nest-result item-active-property #() #t)) |
313 | (update-result result nest-term nest-result))) |
314 | ;; 12.8 |
315 | (for-each |
316 | (lambda (expanded-item) |
317 | (let* ((item-active-property |
318 | (iri-compaction |
319 | active-context inverse-context expanded-property |
320 | #:value expanded-item |
321 | #:vocab? #t |
322 | #:reverse? inside-reverse? |
323 | #:processing-mode processing-mode)) |
324 | (def (term-definition-ref active-context item-active-property)) |
325 | (nest-term (if (term-definition? def) (term-definition-nest def) #f)) |
326 | ;; 12.8.3 |
327 | (nest-result result) |
328 | ;; 12.8.4 |
329 | (container (container-mapping active-context item-active-property)) |
330 | ;; 12.8.5 |
331 | (as-array? (or (member "@set" container) |
332 | (equal? item-active-property "@list") |
333 | (equal? item-active-property "@graph") |
334 | (not compact-arrays?))) |
335 | ;; 12.8.6 |
336 | (compacted-item |
337 | (compaction active-context inverse-context item-active-property |
338 | (if (json-has-key? expanded-item "@list") |
339 | (assoc-ref expanded-item "@list") |
340 | (if (graph-object? expanded-item) |
341 | (assoc-ref expanded-item "@graph") |
342 | expanded-item)) |
343 | #:compact-arrays? compact-arrays? |
344 | #:ordered? ordered?))) |
345 | (pk 'item-active-property item-active-property) |
346 | ;; 12.8.2 |
347 | (when (not-null-or-false nest-term) |
348 | (unless (or (equal? nest-term "@nest") |
349 | (equal? (expand-key active-context nest-term) "@nest")) |
350 | (throw 'invalid-@nest-value)) |
351 | (set! nest-result |
352 | (if (json-has-key? result nest-term) |
353 | (assoc-ref result nest-term) |
354 | '()))) |
355 | (cond |
356 | ;; 12.8.7 |
357 | ((json-has-key? expanded-item "@list") |
358 | ;; 12.8.7.1 |
359 | (unless (json-array? compacted-item) |
360 | (set! compacted-item `#(,compacted-item))) |
361 | (if (member "@list" container) |
362 | ;; 12.8.7.3 |
363 | (set! nest-result |
364 | (alist-set nest-result item-active-property compacted-item)) |
365 | ;; 12.8.7.2 |
366 | (begin |
367 | (set! compacted-item |
368 | `((,(iri-compaction active-context inverse-context |
369 | "@list" #:vocab? #t |
370 | #:processing-mode processing-mode) . |
371 | ,compacted-item))) |
372 | (when (json-has-key? expanded-item "@index") |
373 | (set! compacted-item |
374 | (alist-set |
375 | compacted-item |
376 | (iri-compaction active-context inverse-context |
377 | "@index" #:vocab? #t |
378 | #:processing-mode processing-mode) |
379 | (assoc-ref expanded-item "@index")))) |
380 | (set! nest-result |
381 | (add-value nest-result item-active-property compacted-item |
382 | as-array?)))) |
383 | (update-result result nest-term nest-result)) |
384 | ;; 12.8.8 |
385 | ((graph-object? expanded-item) |
386 | (cond |
387 | ;; 12.8.8.1 |
388 | ((and (member "@id" container) |
389 | (member "@graph" container)) |
390 | (let* ((map-object |
391 | (or (not-null-or-false (assoc-ref nest-result item-active-property)) '())) |
392 | (map-key |
393 | (if (json-has-key? expanded-item "@id") |
394 | (assoc-ref expanded-item "@id") |
395 | "@none")) |
396 | (map-key |
397 | (iri-compaction |
398 | active-context inverse-context map-key |
399 | #:vocab? (not (json-has-key? expanded-item "@id")) |
400 | #:processing-mode processing-mode))) |
401 | ;; 12.8.8.1.3 |
402 | (set! map-object |
403 | (add-value map-object map-key compacted-item as-array?)) |
404 | (set! nest-result |
405 | (alist-set nest-result item-active-property map-object)) |
406 | (update-result result nest-term nest-result))) |
407 | ;; 12.8.8.2 |
408 | ((and (member "@graph" container) |
409 | (member "@index" container) |
410 | (simple-graph-object? expanded-item)) |
411 | (let ((map-object |
412 | (or (not-null-or-false (assoc-ref nest-result item-active-property)) '())) |
413 | (map-key |
414 | (if (json-has-key? expanded-item "@index") |
415 | (assoc-ref expanded-item "@index") |
416 | "@none"))) |
417 | ;; 12.8.8.2.3 |
418 | (set! map-object |
419 | (add-value map-object map-key compacted-item as-array?)) |
420 | (set! nest-result |
421 | (alist-set nest-result item-active-property map-object)) |
422 | (update-result result nest-term nest-result))) |
423 | ;; 12.8.8.3 |
424 | ((and (member "@graph" container) |
425 | (simple-graph-object? expanded-item)) |
426 | (when (and (json-array? compacted-item) |
427 | (> (array-length compacted-item) 1)) |
428 | (set! compacted-item |
429 | `((,(iri-compaction active-context inverse-context "@included" |
430 | #:vocab? #t #:processing-mode processing-mode) . |
431 | ,compacted-item)))) |
432 | (set! nest-result |
433 | (add-value nest-result item-active-property compacted-item |
434 | as-array?)) |
435 | (update-result result nest-term nest-result)) |
436 | ;; 12.8.8.4 |
437 | (else |
438 | ;; 12.8.8.4.1 |
439 | (set! compacted-item |
440 | `((,(iri-compaction active-context inverse-context "@graph" |
441 | #:vocab? #t #:processing-mode processing-mode) . |
442 | ,compacted-item))) |
443 | ;; 12.8.8.4.2 |
444 | (when (json-has-key? expanded-item "@id") |
445 | (set! compacted-item |
446 | (alist-set |
447 | compacted-item |
448 | (iri-compaction active-context inverse-context "@id" |
449 | #:vocab? #t |
450 | #:processing-mode processing-mode) |
451 | (iri-compaction active-context inverse-context |
452 | (assoc-ref expanded-item "@id") |
453 | #:vocab? #f |
454 | #:processing-mode processing-mode)))) |
455 | ;; 12.8.8.4.3 |
456 | (when (json-has-key? expanded-item "@index") |
457 | (set! compacted-item |
458 | (alist-set |
459 | compacted-item |
460 | (iri-compaction active-context inverse-context "@index" |
461 | #:vocab? #t |
462 | #:processing-mode processing-mode) |
463 | (assoc-ref expanded-item "@index")))) |
464 | ;; 12.8.8.4.4 |
465 | (set! nest-result |
466 | (add-value nest-result item-active-property compacted-item |
467 | as-array?)) |
468 | (update-result result nest-term nest-result)))) |
469 | ;; 12.8.9 |
470 | ((and (not (member "@graph" container)) |
471 | (or (member "@language" container) |
472 | (member "@index" container) |
473 | (member "@id" container) |
474 | (member "@type" container))) |
475 | ;; 12.8.9.1 |
476 | (let* ((map-object (or (assoc-ref nest-result item-active-property) |
477 | '())) |
478 | ;; 12.8.9.2 |
479 | (container-key (iri-compaction active-context inverse-context |
480 | (cond |
481 | ((member "@language" container) "@language") |
482 | ((member "@index" container) "@index") |
483 | ((member "@id" container) "@id") |
484 | (else "@type")) |
485 | #:vocab? #t)) |
486 | (map-key json-null) |
487 | ;; 12.8.9.3 |
488 | (def (term-definition-ref active-context item-active-property)) |
489 | (index-key (or (and (term-definition? def) |
490 | (term-definition-index def)) |
491 | "@index"))) |
492 | (cond |
493 | ;; 12.8.9.4 |
494 | ((and (member "@language" container) |
495 | (json-has-key? expanded-item "@value")) |
496 | (set! compacted-item (assoc-ref expanded-item "@value")) |
497 | (when (json-has-key? expanded-item "@language") |
498 | (set! map-key (assoc-ref expanded-item "@language")))) |
499 | ;; 12.8.9.5 |
500 | ((and (member "@index" container) |
501 | (equal? index-key "@index")) |
502 | (when (json-has-key? expanded-item "@index") |
503 | (set! map-key (assoc-ref expanded-item "@index")))) |
504 | ;; 12.8.9.6 |
505 | ((member "@index" container) |
506 | ;; 12.8.9.6.1 |
507 | (set! container-key |
508 | (iri-compaction active-context inverse-context index-key |
509 | #:vocab? #t)) |
510 | ;; 12.8.9.6.2 |
511 | (let* ((keys (assoc-ref compacted-item container-key)) |
512 | (keys (if (json-array? keys) |
513 | (array->list keys) |
514 | (list keys))) |
515 | (key (and (not (null? keys)) (not-null-or-false (car keys)))) |
516 | (remaining (if key (cdr keys) '()))) |
517 | (when key |
518 | (unless (string? key) |
519 | (set! remaining keys) |
520 | (set! key #f)) |
521 | (when key |
522 | (set! map-key key))) |
523 | ;; 12.8.9.6.3 |
524 | (if (null? remaining) |
525 | (when (json-has-key? compacted-item container-key) |
526 | (set! compacted-item |
527 | (alist-remove compacted-item container-key))) |
528 | (set! compacted-item |
529 | (alist-set compacted-item container-key |
530 | (if (= (length remaining) 1) |
531 | (car remaining) |
532 | (list->array 1 remaining))))))) |
533 | ;; 12.8.9.7 |
534 | ((member "@id" container) |
535 | (when (json-has-key? compacted-item container-key) |
536 | (set! map-key (assoc-ref compacted-item container-key)) |
537 | (set! compacted-item (alist-remove compacted-item container-key)))) |
538 | ;; 12.8.9.8 |
539 | ((member "@type" container) |
540 | (let* ((keys (assoc-ref compacted-item container-key)) |
541 | (keys (if (json-array? keys) |
542 | (array->list keys) |
543 | (list keys))) |
544 | (key (and (not (null? keys)) (not-null-or-false (car keys)))) |
545 | (remaining (if key (cdr keys) '()))) |
546 | ;; 12.8.9.8.1 |
547 | (when (not-null-or-false key) |
548 | (set! map-key key)) |
549 | ;; 12.8.9.8.2 |
550 | (if (null? remaining) |
551 | (set! compacted-item |
552 | (alist-remove compacted-item container-key)) |
553 | (set! compacted-item |
554 | (alist-set compacted-item container-key |
555 | (if (= (length remaining) 1) |
556 | (car remaining) |
557 | (list->array 1 remaining))))) |
558 | (when (and (= (length compacted-item) 1) |
559 | (equal? |
560 | (expand-key active-context (car (car compacted-item))) |
561 | "@id")) |
562 | (set! compacted-item |
563 | (compaction |
564 | active-context inverse-context item-active-property |
565 | `(("@id" . ,(assoc-ref expanded-item "@id"))))))))) |
566 | ;; 12.8.9.9 |
567 | (when (json-null? map-key) |
568 | (set! map-key (iri-compaction active-context inverse-context |
569 | "@none" #:vocab? #t))) |
570 | ;; 12.8.9.10 |
571 | (set! map-object |
572 | (add-value map-object map-key compacted-item as-array?)) |
573 | (set! nest-result |
574 | (alist-set nest-result item-active-property map-object)) |
575 | (update-result result nest-term nest-result))) |
576 | ;; 12.8.10 |
577 | (else |
578 | (set! nest-result |
579 | (add-value nest-result item-active-property |
580 | compacted-item as-array?)) |
581 | (update-result result nest-term nest-result))))) |
582 | (array->list expanded-value)))) |
583 | result) |
584 |