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
  (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