Support parsing of expanded term definitions
Also adds some descriptive conditions used in parsing; and changes the format of context hash-tables.
This commit is contained in:
parent
a5567e043e
commit
29a9814008
106
src/json-ld.lisp
106
src/json-ld.lisp
|
@ -48,13 +48,19 @@
|
||||||
"Parse a JSON “node object” (as decoded by YASON into a hash-TABLE."
|
"Parse a JSON “node object” (as decoded by YASON into a hash-TABLE."
|
||||||
(let ((ctx (parse-context (gethash "@context" table) ctx)))
|
(let ((ctx (parse-context (gethash "@context" table) ctx)))
|
||||||
(maphash
|
(maphash
|
||||||
(lambda (key val)
|
(lambda (old-key val)
|
||||||
(alexandria:when-let ((key-iri (gethash key ctx)))
|
(let* ((key-ctx (gethash old-key ctx))
|
||||||
(remhash key table)
|
(key-iri (getf key-ctx :id))
|
||||||
(setf (gethash key-iri table)
|
(key-type (getf key-ctx :type))
|
||||||
(parse-item
|
(new-key (or key-iri old-key)))
|
||||||
val (or (and (hash-table-p val) (alexandria:copy-hash-table ctx))
|
(when key-ctx
|
||||||
ctx)))))
|
(if (not (equal old-key new-key))
|
||||||
|
(remhash old-key table))
|
||||||
|
(setf (gethash new-key table)
|
||||||
|
(parse-item
|
||||||
|
val
|
||||||
|
(or (and (hash-table-p val) (alexandria:copy-hash-table ctx))
|
||||||
|
ctx))))))
|
||||||
table)
|
table)
|
||||||
table))
|
table))
|
||||||
|
|
||||||
|
@ -87,8 +93,7 @@ been aprsed into CTX. See UNCOMPACT-IRI and COMPACT-IRI-P for more info on IRIs.
|
||||||
((eq (length now-unresolved)
|
((eq (length now-unresolved)
|
||||||
(length unresolved-terms))
|
(length unresolved-terms))
|
||||||
(values ctx now-unresolved)
|
(values ctx now-unresolved)
|
||||||
(error 'unresolved :message
|
(error 'iri-unresolved :unresolved now-unresolved))
|
||||||
(format nil "Compact IRI could not be resolved: ~A" now-unresolved)))
|
|
||||||
(T
|
(T
|
||||||
(repeat-parse-context ctx now-unresolved)))))
|
(repeat-parse-context ctx now-unresolved)))))
|
||||||
|
|
||||||
|
@ -122,17 +127,17 @@ IRI values whose prefix hasn’t yet been parsed into CTX."
|
||||||
(lambda (term val)
|
(lambda (term val)
|
||||||
(let* ((iri
|
(let* ((iri
|
||||||
(typecase val
|
(typecase val
|
||||||
(string val)
|
(string (when (iri-p val) val))
|
||||||
(hash-table (gethash "@id" val))))
|
(hash-table (gethash "@id" val))))
|
||||||
(noncompact-iri
|
(uncompacted-iri (ignore-errors (uncompact-iri iri ctx)))
|
||||||
(if (ld-keyword-p iri)
|
(type
|
||||||
iri
|
(typecase val
|
||||||
(uncompact-iri iri ctx))))
|
(string nil)
|
||||||
(cond ((and (compacted-iri-p iri)
|
(hash-table (gethash "@type" val)))))
|
||||||
(not noncompact-iri))
|
(cond ((and iri (not uncompacted-iri))
|
||||||
(push (cons term iri) unresolvable))
|
(push (cons term iri) unresolvable))
|
||||||
((not (gethash term ctx))
|
(T
|
||||||
(setf (gethash term ctx) noncompact-iri)))))
|
(setf (gethash term ctx) (list :id uncompacted-iri :type type))))))
|
||||||
table)
|
table)
|
||||||
unresolvable))
|
unresolvable))
|
||||||
|
|
||||||
|
@ -157,16 +162,63 @@ https://www.w3.org/TR/json-ld11/#compact-iris"
|
||||||
(if (compacted-iri-p iri)
|
(if (compacted-iri-p iri)
|
||||||
(destructuring-bind (prefix suffix)
|
(destructuring-bind (prefix suffix)
|
||||||
(str:split #\: iri)
|
(str:split #\: iri)
|
||||||
(alexandria:when-let ((prefix-iri (gethash (string-downcase prefix) ctx)))
|
(let* ((prefix-ctx (gethash (string-downcase prefix) ctx))
|
||||||
(format nil "~A~A" prefix-iri suffix)))
|
(prefix-iri (when prefix-ctx (getf prefix-ctx :id))))
|
||||||
iri))
|
(if prefix-iri
|
||||||
|
(format nil "~A~A" prefix-iri suffix)
|
||||||
|
(error 'iri-prefix-not-found :iri prefix))))
|
||||||
|
(if (not (iri-p iri))
|
||||||
|
(error 'not-iri :iri iri)
|
||||||
|
iri)))
|
||||||
|
|
||||||
(defun compacted-iri-p (iri)
|
(defun iri-p (str)
|
||||||
"Return whether or not an IRI is in compacted “prefix:suffix” form.
|
"Return whether or not a string is an IRI, compacted or otherwise."
|
||||||
|
(or (uncompacted-iri-p str)
|
||||||
|
(compacted-iri-p str)))
|
||||||
|
|
||||||
|
(defun uncompacted-iri-p (str)
|
||||||
|
"Return whether or not a string is an orindary IRI."
|
||||||
|
(search "://" str))
|
||||||
|
|
||||||
|
(defun compacted-iri-p (str)
|
||||||
|
"Return whether or not a string is an IRI in compacted “prefix:suffix” form.
|
||||||
https://www.w3.org/TR/json-ld11/#compact-iris"
|
https://www.w3.org/TR/json-ld11/#compact-iris"
|
||||||
(and (find #\: iri)
|
(and (find #\: str)
|
||||||
(not (search "://" iri))
|
(not (search "://" str))
|
||||||
(not (equal iri "_:"))))
|
(not (equal str "_:"))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; Conditions
|
||||||
|
;;; ————————————————————————————————————————
|
||||||
|
(define-condition iri-error (error)
|
||||||
|
((iri :initarg :iri :initform nil :accessor iri-error-iri)))
|
||||||
|
|
||||||
|
(define-condition not-iri (iri-error) ()
|
||||||
|
(:report
|
||||||
|
(lambda (condition stream)
|
||||||
|
(format stream "“~A” is not a valid IRI." (iri-error-iri condition))))
|
||||||
|
(:documentation
|
||||||
|
"A string containing an IRI was expected, but something else was provided."))
|
||||||
|
|
||||||
|
(define-condition iri-prefix-not-found (iri-error) ()
|
||||||
|
(:report
|
||||||
|
(lambda (condition stream)
|
||||||
|
(format stream "Failed to find IRI prefix in context: ~A" (iri-error-iri condition))))
|
||||||
|
(:documentation
|
||||||
|
"Attempted to resolve a compact IRI’s prefix, but its prefix hasn’t yet been
|
||||||
|
defined in the context."))
|
||||||
|
|
||||||
|
(define-condition context-error (error)
|
||||||
|
((unresolved :initarg :unresolved :initform nil :accessor context-error-unresolved)))
|
||||||
|
|
||||||
|
(define-condition iri-unresolved (context-error) ()
|
||||||
|
(:report
|
||||||
|
(lambda (condition stream)
|
||||||
|
(format nil "Compact IRI(s) in context could not be resolved: ~A"
|
||||||
|
(context-error-unresolved condition))))
|
||||||
|
(:documentation
|
||||||
|
"Compact IRI(s)’ prefixes couldn’t be resolved, having analyzed all context items."))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue