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