guile-jsonld/jsonld/compaction.scm

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