;;;; Copyright (C) 2019, 2020 Julien Lepiller ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 3 of the License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ;;;; (define-module (jsonld expansion) #:use-module (jsonld context) #:use-module (jsonld context-processing) #:use-module (jsonld iri-expansion) #:use-module (jsonld value-expansion) #:use-module (jsonld json) #:use-module (jsonld options) #:use-module (ice-9 match) #:use-module (srfi srfi-1) #:use-module (web uri) #:export (expansion)) ;; 13.7 (define* (execute-when-language active-context key value expanded-property active-property expanded-value result options) ;; 13.7.2 (let ((direction (active-context-direction active-context))) ;; 13.7.1 (set! expanded-value '()) ;; 13.7.3 (when (and (term-definition-ref active-context key)) (set! direction (term-definition-direction (term-definition-ref active-context key))) (when (equal? direction #f) (set! direction (active-context-direction active-context)))) ;; 13.7.4 (for-each-pair (lambda (language language-value) ;; 13.7.4.1 (if (json-array? language-value) (set! language-value (array->list language-value)) (set! language-value (list language-value))) ;; 13.7.4.2 (for-each (lambda (item) ;; 13.7.4.2.1 (unless (equal? item #nil) ;; 13.7.4.2.2 (unless (string? item) (throw 'invalid-language-map-value)) ;; 13.7.4.2.3 (let ((v `(("@value" . ,item) ("@language" . ,(string-downcase language))))) ;; TODO: if @language is not a bcp-47 one, we should issue a warning (when (equal? (expand-key active-context language) "@none") (set! v (alist-remove v "@language"))) (when direction (set! v (alist-set v "@direction" direction))) (set! expanded-value (append expanded-value (list v)))))) language-value)) (if (jsonld-options-ordered? options) (alist-sort-by-key value) value)) (set! expanded-value (list->array 1 expanded-value))) `(("expanded-value" . ,expanded-value))) ;; 13.8 (define* (execute-when-index active-context key value expanded-property active-property expanded-value result container-mapping base-url options) ;; 13.8.1 (set! expanded-value '()) ;; 13.8.2 (let ((index-key (if (term-definition-ref active-context key) (or (term-definition-index (term-definition-ref active-context key)) "@index") "@index")) ;; Should not be #nil, if it fails because of that, there's a weird corner case (map-context #nil) (expanded-index #nil)) ;; 13.8.3 (for-each-pair (lambda (index index-value) ;; 13.8.3.1 (when (or (member "@id" container-mapping) (member "@type" container-mapping)) (set! map-context (or (active-context-previous active-context) active-context))) ;; 13.8.3.2 (when (and (member "@type" container-mapping) (term-definition-ref map-context index) (not (equal? (term-definition-context (term-definition-ref map-context index)) #f))) (set! map-context (context-processing map-context (term-definition-context (term-definition-ref map-context index)) (term-definition-base-url (term-definition-ref map-context index)) #:options options))) ;; 13.8.3.3 (when (equal? map-context #nil) (set! map-context active-context)) ;; 13.8.3.4 (set! expanded-index (assoc-ref (iri-expansion active-context index ; XXX: or map-context? #:vocab? #t #:options options) "iri")) ;; 13.8.3.5 (unless (json-array? index-value) (set! index-value `#(,index-value))) ;; 13.8.3.6 (set! index-value (expansion map-context key index-value base-url #:options options)) ;; 13.8.3.7 (for-each (lambda (item) ;; 13.8.3.7.1 (when (member "@graph" container-mapping) (set! item (if (json-has-key? item "@graph") item `(("@graph" . ,(if (json-array? item) item `#(,item))))))) (cond ;; 13.8.3.7.2 ((and (member "@index" container-mapping) (not (equal? index-key "@index")) (not (equal? expanded-index "@none"))) (let* (;; 13.8.3.7.2.1 (re-expanded-index (value-expansion active-context index-key index)) ;; 13.8.3.7.2.2 (expanded-index-key (assoc-ref (iri-expansion active-context index-key #:vocab? #t #:options options) "iri")) ;; 13.8.3.7.2.3 (index-key-values (assoc-ref item expanded-index-key)) (index-key-values (or index-key-values #())) (index-key-values (if (json-array? index-key-values) (array->list index-key-values) (list index-key-values))) (index-property-values (cons re-expanded-index index-key-values))) (set! item (alist-set item expanded-index-key (list->array 1 index-property-values))) (when (json-has-key? item "@value") (unless (null? (filter (lambda (kv) (not (equal? (car kv) "@value"))) item)) (throw 'invalid-value-object))))) ;; 13.8.3.7.3 ((and (member "@index" container-mapping) (not (json-has-key? item "@index")) (not (equal? expanded-index "@none"))) (set! item (alist-set item "@index" index))) ;; 13.8.3.7.4 ((and (member "@id" container-mapping) (not (json-has-key? item "@id")) (not (equal? expanded-index "@none"))) (set! expanded-index (assoc-ref (iri-expansion active-context index #:vocab? #f #:document-relative? #t #:options options) "iri")) (set! item (alist-set item "@id" expanded-index))) ;; 13.8.3.7.5 ((member "@type" container-mapping) (let* ((types (assoc-ref item "@type")) (types (or types #())) (types (if (json-array? types) (array->list types) (list types))) (types (if (equal? expanded-index "@none") types (cons expanded-index types)))) (unless (equal? (length types) 0) (set! item (alist-set item "@type" (list->array 1 types)))))) (else #t)) (set! expanded-value (append expanded-value (list item)))) (array->list index-value))) (if (jsonld-options-ordered? options) (alist-sort-by-key value) value)) (set! expanded-value (list->array 1 expanded-value)) `(("expanded-value" . ,expanded-value)))) ;; 13.4 (define (execute-when-keyword active-context key value expanded-property active-property expanded-value continue? result type-scoped-context input-type nests base-url options) (cond ;; 13.4.1 ((equal? active-property "@reverse") (throw 'invalid-reverse-property-map)) ;; 13.4.2 ((and (json-has-key? result expanded-property) (or (processing-mode-1.0? (jsonld-options-processing-mode options)) (not (member expanded-property '("@included" "@type"))))) (throw 'colliding-keywords)) ;; 13.4.3 ((equal? expanded-property "@id") (unless (or (string? value) (and (jsonld-options-frame-expansion? options) (or (equal? value '()) (not (equal? value #())) (string-array? value)))) (throw 'invalid-@id-value)) (cond ((string? value) (set! expanded-value (assoc-ref (iri-expansion active-context value #:document-relative? #t #:options options) "iri")) (when (jsonld-options-frame-expansion? options) (set! expanded-value `#(,expanded-value)))) ((equal? value '()) ;; XXX: is the the right thing to do? (set! expanded-value `#(()))) ((json-array? value) (set! expanded-value (map (lambda (v) (assoc-ref (iri-expansion active-context v #:document-relative? #t #:options options) "iri")) (array->list value))) (set! expanded-value (list->array 1 expanded-value))))) ;; 13.4.4 ((equal? expanded-property "@type") ;; 13.4.4.1 (unless (or (string? value) (string-array? value) (and (jsonld-options-frame-expansion? options) (or (equal? value '()) (and (json-object? value) (json-has-key? value "@default"))))) (throw 'invalid-type-value)) (cond ;; 13.4.4.2 ((equal? value '()) (set! expanded-value '())) ;; 13.4.4.3 ((json-has-key? value "@default") (let ((iri (assoc-ref (iri-expansion type-scoped-context (assoc-ref value "@default") #:vocab? #t #:document-relative? #t #:options options) "iri"))) (unless (absolute-iri? iri) (throw 'invalid-type-value)) (set! expanded-value (alist-set value "@default" iri)))) ;; 13.4.4.4 ((string? value) (set! expanded-value (assoc-ref (iri-expansion type-scoped-context value #:vocab? #t #:document-relative? #t #:options options) "iri"))) ((string-array? value) (set! expanded-value (list->array 1 (map (lambda (v) (assoc-ref (iri-expansion type-scoped-context v #:vocab? #t #:document-relative? #t #:options options) "iri")) (array->list value)))))) ;; 13.4.4.5 (when (json-has-key? result "@type") (set! expanded-value (list->array 1 (append (array->list (if (json-array? (assoc-ref result "@type")) (assoc-ref result "@type") `#(,(assoc-ref result "@type")))) (array->list (if (json-array? expanded-value) expanded-value `#(,expanded-value)))))))) ;; 13.4.5 ((equal? expanded-property "@graph") (set! expanded-value (expansion active-context "@graph" value base-url #:options options)) (unless (json-array? expanded-value) (set! expanded-value `#(,expanded-value)))) ;; 13.4.6 ((equal? expanded-property "@included") (if (processing-mode-1.0? (jsonld-options-processing-mode options)) (set! continue? #f)) (begin ;; 13.4.6.2 (set! expanded-value (expansion active-context active-property value base-url #:options options)) (if (json-array? expanded-value) (set! expanded-value (array->list expanded-value)) (set! expanded-value (list expanded-value))) ;; 13.4.6.3 (unless (null? (filter (lambda (v) (not (node-object? v))) expanded-value)) (throw 'invalid-@included-value)) ;; 13.4.6.4 (set! expanded-value (append (if (json-has-key? result "@included") (array->list (assoc-ref result "@included")) '()) expanded-value)) (set! expanded-value (list->array 1 expanded-value)))) ;; 13.4.7 ((equal? expanded-property "@value") ;; 13.4.7.1 (if (equal? input-type "@json") (begin (set! expanded-value value) (when (processing-mode-1.0? (jsonld-options-processing-mode options)) (throw 'invalid-value-object-value))) ;; 13.4.7.2 (begin (unless (or (scalar? value) (equal? value #nil) (and (jsonld-options-frame-expansion? options) (or (equal? value '()) (scalar-array? value)))) (throw 'invalid-value-object-value)))) ;; 13.4.7.3 (set! expanded-value value) (when (jsonld-options-frame-expansion? options) (when (equal? expanded-value '()) (set! expanded-value #(()))) (when (scalar? expanded-value) (set! expanded-value #(,expanded-value)))) ;; 13.4.7.4 (when (equal? expanded-value #nil) (set! continue? #f) (set! result (alist-set result "@value" #nil)))) ;; 13.4.8 ((equal? expanded-property "@language") (unless (or (string? value) (and (jsonld-options-frame-expansion? options) (or (string-array? value) (equal? value '())))) (throw 'invalid-language-tagged-string)) ;; TODO: warning when value is to bcp-47 compliant (cond ((string? value) (set! expanded-value (string-downcase value)) (when (jsonld-options-frame-expansion? options) (set! expanded-value `#(,expanded-value)))) ((equal? value '()) (set! expanded-value #(()))) ((string-array? value) (set! expanded-value value)))) ;; 13.4.9 ((equal? expanded-property "@direction") (if (processing-mode-1.0? (jsonld-options-processing-mode options)) (set! continue? #f) (begin (unless (or (equal? value "ltr") (equal? value "rtl") (and (jsonld-options-frame-expansion? options) (or (string-array? value) (equal? value '())))) (throw 'invalid-base-direction)) (cond ((string? value) (set! expanded-value value) (when (jsonld-options-frame-expansion? options) (set! expanded-value `#(,expanded-value)))) ((equal? value '()) (set! expanded-value #(()))) ((string-array? value) (set! expanded-value value)))))) ;; 13.4.10 ((equal? expanded-property "@index") (if (string? value) (set! expanded-value value) (throw 'invalid-@index-value))) ;; 13.4.11 ((equal? expanded-property "@list") (if (or (equal? active-property "@graph") (equal? active-property #nil)) ;; 13.4.11.1 (set! continue? #f) (begin ;; 13.4.11.2 (set! expanded-value (expansion active-context active-property value base-url #:options options)) ;; Not in spec, but expected from the tests and implemented elsewhere (unless (json-array? expanded-value) (set! expanded-value `#(,expanded-value)))))) ;; 13.4.12 ((equal? expanded-property "@set") (set! expanded-value (expansion active-context active-property value base-url #:options options))) ;; 13.4.13 ((equal? expanded-property "@reverse") (unless (json-object? value) (throw 'invalid-@reverse-value)) ;; 13.4.13.2 (set! expanded-value (expansion active-context "@reverse" value base-url #:options options)) ;; 13.4.13.3 (when (json-has-key? expanded-value "@reverse") (for-each-pair (lambda (property item) (if (json-has-key? result property) (set! result (alist-set result property (list->array 1 (append (array->list (assoc-ref result property)) (list item))))) (set! result (alist-set result property (if (json-array? item) item `#(,item)))))) (assoc-ref expanded-value "@reverse"))) ;; 13.4.13.4 (unless (null? (filter (lambda (p) (not (equal? (car p) "@reverse"))) expanded-value)) ;; 13.4.13.4.1 and 13.4.13.4.2 (let ((reverse-map (if (json-has-key? result "@reverse") (assoc-ref result "@reverse") '()))) (for-each-pair ;; 13.4.13.4.3 (lambda (property items) (unless (equal? property "@reverse") (for-each ;; 13.4.13.4.3.1 (lambda (item) ;; 13.4.13.4.3.1.1 (when (json-has-key? item "@value") (throw 'invalid-reverse-property-value)) (when (json-has-key? item "@list") (throw 'invalid-reverse-property-value)) (if (json-has-key? reverse-map property) ;; 13.4.13.4.3.1.2 (set! reverse-map (alist-set reverse-map property (list->array 1 (append (array->list (assoc-ref reverse-map property)) (list item))))) ;; 13.4.13.4.3.1.3 (set! reverse-map (alist-set reverse-map property `#(,item))))) (array->list items)))) expanded-value) (set! result (alist-set result "@reverse" reverse-map)))) ;; 13.4.13.5 (set! continue? #f)) ;; 13.4.14 ((equal? expanded-property "@nest") (set! nests (cons key (or nests '()))) (set! continue? #f)) ;; 13.4.15 ((and (jsonld-options-frame-expansion? options) (member expanded-property '("@explicit" "@default" "@embed" "@omitDefault" "@requireAll"))) (set! expanded-value (expansion active-context active-property value base-url #:options options)))) ;; 13.4.16 (unless (or (not continue?) (and (equal? expanded-value #nil) (equal? expanded-property "@value") (equal? input-type "@json"))) (set! result (alist-set result expanded-property expanded-value))) `(("result" . ,result) ("nests" . ,nests))) (define (execute-13 active-context active-property element property-scoped-context type-scoped-context result nests input-type base-url options) (for-each-pair (lambda (key value) ;; 13.1: skip is @context (unless (equal? key "@context") ;; 13.2 (let ((expanded-property (assoc-ref (iri-expansion active-context key #:vocab? #t #:options options) "iri")) (expanded-value #nil) ;; whether we continue evaluating this key or not. #f means go ;; immediately to processing the next key-value pair. (continue? #t) (container-mapping #f)) (cond ;; 13.3 ((or (equal? expanded-property #nil) (not (or (json-keyword? expanded-property) (string-index expanded-property #\:)))) (set! continue? #f)) ;; 13.4 ((json-keyword? expanded-property) (let ((exec-result (execute-when-keyword active-context key value expanded-property active-property expanded-value continue? result type-scoped-context input-type nests base-url options))) (set! result (assoc-ref exec-result "result")) (set! nests (assoc-ref exec-result "nests"))) (set! continue? #f)) ;; 13.5 (else (set! container-mapping (let* ((def (term-definition-ref active-context key)) (container (and (term-definition? def) (term-definition-container def)))) (and def (if (json-array? container) (array->list container) (if container (list container) '()))))) (cond ;; 13.6 ((and (term-definition-ref active-context key) (equal? (term-definition-type (term-definition-ref active-context key)) "@json")) (set! expanded-value `(("@value" . ,value) ("@type" . "@json")))) ;; 13.7 ((and container-mapping (member "@language" container-mapping) (json-object? value)) (let ((exec-result (execute-when-language active-context key value expanded-property active-property expanded-value result options))) (set! expanded-value (assoc-ref exec-result "expanded-value")))) ;; 13.8 ((and container-mapping (or (member "@index" container-mapping) (member "@type" container-mapping) (member "@id" container-mapping)) (json-object? value)) (let ((exec-result (execute-when-index active-context key value expanded-property active-property expanded-value result container-mapping base-url options))) (set! expanded-value (assoc-ref exec-result "expanded-value")))) ;; 13.9 (else (set! expanded-value (expansion active-context key value base-url #:options options)))))) ;; 13.10 and previous (via continue?): do we process further, or ;; go to the next key immediately? (when (and continue? (not (equal? expanded-value #nil))) ;; 13.11 (when (and container-mapping (member "@list" container-mapping) (not (json-has-key? expanded-value "@list"))) (set! expanded-value `(("@list" . ,(if (json-array? expanded-value) expanded-value `#(,expanded-value)))))) ;; 13.12 (when (and container-mapping (member "@graph" container-mapping) (not (member "@id" container-mapping)) (not (member "@index" container-mapping))) (if (json-array? expanded-value) (set! expanded-value (array->list expanded-value)) (set! expanded-value (list expanded-value))) (set! expanded-value (map (lambda (ev) `(("@graph" . ,(if (json-array? ev) ev `#(,ev))))) expanded-value)) (set! expanded-value (list->array 1 expanded-value))) ;; 13.13 (if (and (term-definition-ref active-context key) (term-definition-reverse? (term-definition-ref active-context key))) ;; 13.13.1 and 13.13.2 (let ((reverse-map (if (json-has-key? result "@reverse") (assoc-ref result "@reverse") '()))) (for-each ;; 13.13.4 (lambda (item) ;; 13.13.4.1 (when (json-has-key? item "@value") (throw 'invalid-reverse-property-value)) ;; 13.13.4.1 (when (json-has-key? item "@list") (throw 'invalid-reverse-property-value)) ;; 13.13.4.2 and 13.13.4.3 (set! reverse-map (alist-set reverse-map expanded-property (list->array 1 (append (array->list (if (json-has-key? reverse-map expanded-property) (assoc-ref reverse-map expanded-property) #())) (list item)))))) ;; 13.13.3 (if (json-array? expanded-value) (array->list expanded-value) (list expanded-value))) (set! result (alist-set result "@reverse" reverse-map))) ;; 13.14 (set! result (alist-set result expanded-property (list->array 1 (if (json-array? expanded-value) (append (array->list (if (json-has-key? result expanded-property) (assoc-ref result expanded-property) #())) (array->list expanded-value)) (append (array->list (if (json-has-key? result expanded-property) (assoc-ref result expanded-property) #())) (list expanded-value))))))))))) (if (jsonld-options-ordered? options) (alist-sort-by-key element) element)) `(("result" . ,result) ("nests" . ,nests))) (define (execute-14 active-context active-property element property-scoped-context type-scoped-context result nests input-type base-url options) ;; 14 (for-each (lambda (nesting-key) ;; 14.1 (let ((nested-values (assoc-ref element nesting-key))) (unless (json-array? nested-values) (set! nested-values `#(,nested-values))) ;; 14.2 (for-each (lambda (nested-value) ;; 14.2.1 (unless (and (json-object? nested-value) ;; XXX: "expand to @value" (not (json-key-expanded-to? active-context nested-value "@value"))) (throw 'invalid-@nest-value)) ;; 14.2.2 (let ((exec-result (execute-13 active-context active-property nested-value property-scoped-context type-scoped-context result '() input-type base-url options))) (set! result (assoc-ref exec-result "result")) (set! nests (assoc-ref exec-result "nests")) (let ((exec-result (execute-14 active-context active-property nested-value property-scoped-context type-scoped-context result nests input-type base-url options))) (set! result (assoc-ref exec-result "result"))))) (array->list nested-values)))) ;; nests was built with cons, so we have to reverse it (reverse nests)) `(("result" . ,result))) (define* (expansion active-context active-property element base-url #:key (from-map? #f) (options (new-jsonld-options))) "Expand a JsonLD document. This is an implementation of the expansion algorithm defined in the JsonLD API specification. See @url{https://www.w3.org/TR/2014/REC-json-ld-api-20140116}." ;; 3 (define property-scoped-context (if (term-definition-ref active-context active-property) ;; can be #nil, so we cannot use `or` here (term-definition-context (term-definition-ref active-context active-property)) #f)) ;; 2 (when (equal? active-property "@default") (set! options (update-jsonld-options options #:frame-expansion? #f))) (cond ;; 1 ((equal? element #nil) #nil) ;; 4 ((scalar? element) (if (member active-property '(#nil "@graph")) ;; 4.1 #nil (begin ;; 4.2 (unless (equal? property-scoped-context #f) (set! active-context (context-processing active-context property-scoped-context base-url))) ;; 4.3 (value-expansion active-context active-property element #:options options)))) ;; 5 ((array? element) ;; 5.1 (let ((result '())) ;; 5.2 (for-each (lambda (item) ;; 5.2.1 (let ((expanded-item (expansion active-context active-property item base-url #:from-map? from-map? #:options options))) ;; 5.2.2 (when (and (term-definition-ref active-context active-property) (term-definition-container (term-definition-ref active-context active-property)) (member "@list" (array->list (term-definition-container (term-definition-ref active-context active-property)))) (json-array? expanded-item)) (set! expanded-item `(("@list" . ,expanded-item)))) ;; 5.2.3 (if (json-array? expanded-item) (set! result (append result (array->list expanded-item))) (unless (equal? expanded-item #nil) (set! result (append result (list expanded-item))))))) (array->list element)) ;; 5.3 (list->array 1 result))) ;; 6 (else ;; 7 (when (active-context-previous active-context) (let ((previous (active-context-previous active-context))) (unless (or from-map? (json-key-expanded-to? active-context element "@value") (and (= (length element) 1) (json-key-expanded-to? previous element "@id"))) (set! active-context (active-context-previous active-context))))) ;; 8 (unless (equal? property-scoped-context #f) (let* ((def (term-definition-ref active-context active-property)) (base-url (if (term-definition? def) (term-definition-base-url def) base-url))) (set! active-context (context-processing active-context property-scoped-context base-url #:override-protected? #t #:options options)))) ;; 9 (when (json-has-key? element "@context") (set! active-context (context-processing active-context (assoc-ref element "@context") base-url #:options options))) ;; 10, 12 (let ((type-scoped-context active-context) (result '()) (nests '()) (input-type #nil) (found-first-entry? #f)) ;; 11 (for-each-pair (lambda (key value) (when (equal? (expand-key active-context key) "@type") ;; 12 (unless found-first-entry? (match value ((json-array? value) (set! input-type (car (reverse (array->list value))))) (_ (set! input-type value))) (set! input-type (assoc-ref (iri-expansion active-context input-type #:vocab? #t) "iri"))) (set! found-first-entry? #t) ;; 11.1 (unless (json-array? value) (set! value `#(,value))) ;; 11.2 (for-each (lambda (term) (when (and (term-definition-ref type-scoped-context term) (not (equal? (term-definition-context (term-definition-ref type-scoped-context term)) #f))) (set! active-context (context-processing active-context (term-definition-context (term-definition-ref type-scoped-context term)) (term-definition-base-url (term-definition-ref type-scoped-context term)) #:propagate? #f #:options options)))) (sort (filter string? (array->list value)) string<=?)))) (alist-sort-by-key element)) ;; 13 (let ((exec-result (execute-13 active-context active-property element property-scoped-context type-scoped-context result nests input-type base-url options))) (set! result (assoc-ref exec-result "result")) (set! nests (assoc-ref exec-result "nests"))) ;; 14 (let ((exec-result (execute-14 active-context active-property element property-scoped-context type-scoped-context result nests input-type base-url options))) (set! result (assoc-ref exec-result "result"))) (cond ;; 15 ((json-has-key? result "@value") (begin ;; 15.1 (unless (null? (filter (lambda (p) (not (member (car p) '("@direction" "@value" "@type" "@language" "@index")))) result)) (throw 'invalid-value-object)) (when (and (or (json-has-key? result "@language") (json-has-key? result "@direction")) (json-has-key? result "@type")) (throw 'invalid-value-object)) ;; 15.2 (unless (equal? (assoc-ref result "@type") "@json") ;; 15.3 (when (equal? (assoc-ref result "@value") #nil) (set! result #nil)) ;; 15.4 (unless (or (string? (assoc-ref result "@value")) (not (json-has-key? result "@language"))) (throw 'invalid-language-tagged-value)) ;; 15.5 (unless (or (not (json-has-key? result "@type")) (absolute-iri? (assoc-ref result "@type"))) ;; XXX: what if it's a list? is it valid? (throw 'invalid-typed-value))))) ;; 16 ((json-has-key? result "@type") (unless (json-array? (assoc-ref result "@type")) (set! result (alist-set result "@type" `#(,(assoc-ref result "@type")))))) ;; 17 ((json-has-key? result "@list") ;; 17.1 (unless (null? (filter (lambda (p) (not (member (car p) '("@list" "@index")))) result)) (throw 'invalid-set-or-list-object))) ;; 17 ((json-has-key? result "@set") ;; 17.1 (unless (null? (filter (lambda (p) (not (member (car p) '("@set" "@index")))) result)) (throw 'invalid-set-or-list-object)) ;; 17.2 (set! result (assoc-ref result "@set"))) (else #t)) (cond ;; 18 ((and (json-has-key? result "@language") (null? (filter (lambda (p) (not (equal? (car p) "@language"))) result))) (set! result #nil)) ;; 19 ((or (equal? active-property #nil) (equal? active-property "@graph")) (if (or (equal? result '()) (json-has-key? result "@value") (json-has-key? result "@list")) (set! result #nil) (when (and (not (jsonld-options-frame-expansion? options)) (json-has-key? result "@id") (null? (filter (lambda (p) (not (equal? (car p) "@id"))) result))) (set! result #nil)))) ;; 20 (else #t)) result))))