3cb2ef07 |
(in-package :cl-user)
(defpackage yaml.emitter
(:use :cl)
|
9641dc0a |
(:import-from :cffi
|
6acdefbe |
:foreign-free
:null-pointer)
|
9641dc0a |
(:import-from :libyaml.emitter
|
6acdefbe |
:allocate-emitter
:emitter-initialize
:emitter-delete
:set-output
:stream-start-event-initialize
:stream-end-event-initialize
:document-start-event-initialize
:document-end-event-initialize
:scalar-event-initialize
:sequence-start-event-initialize
:sequence-end-event-initialize
:mapping-start-event-initialize
:mapping-end-event-initialize)
|
9641dc0a |
(:import-from :libyaml.event
|
6acdefbe |
:allocate-event
:event-delete)
|
9641dc0a |
(:import-from :libyaml.write-handler
|
6acdefbe |
:*write-handler-callback*
:*write-handler-stream*)
|
90e18416 |
(:export ;; Original interface
|
6acdefbe |
:emit
:emit-to-string
:encode
;; libyaml based interface
:stream-start-event
:stream-end-event
:document-start-event
:document-end-event
:scalar-event
:sequence-start-event
:sequence-end-event
:mapping-start-event
:mapping-end-event
:emit-stream
:emit-document
:emit-sequence
:emit-mapping
:emit-scalar
|
9641dc0a |
:emit-object
:print-scalar
|
6acdefbe |
:with-emitter-to-stream
:with-emitter-to-string)
|
3cb2ef07 |
(:documentation "The YAML emitter."))
(in-package :yaml.emitter)
|
c83aa5d6 |
|
90e18416 |
;;; Encoder functions
(defgeneric encode (value stream)
(:documentation "Write the YAML corresponding to value to a stream."))
(defmethod encode ((true (eql 't)) stream)
"Encode true."
(write-string "true" stream))
(defmethod encode ((true (eql 'nil)) stream)
"Encode false."
(write-string "false" stream))
(defmethod encode ((integer integer) stream)
"Encode an integer."
(princ integer stream))
(defmethod encode ((float float) stream)
"Encode a float."
(princ float stream))
(defmethod encode ((string string) stream)
"Encode a string."
;; (write-string string stream)
(format stream "~s" string))
(defmethod encode ((list list) stream)
"Encode a list."
(write-string "[" stream)
(loop for sublist on list do
(encode (first sublist) stream)
(when (rest sublist)
(write-string ", " stream)))
(write-string "]" stream))
(defmethod encode ((vector vector) stream)
"Encode a vector."
(encode (loop for elem across vector collecting elem) stream))
(defmethod encode ((table hash-table) stream)
"Encode a hash table."
(write-string "{ " stream)
(loop for sublist on (alexandria:hash-table-keys table) do
(let ((key (first sublist)))
(encode key stream)
(write-string ": " stream)
(encode (gethash key table) stream)
(when (rest sublist)
(write-string ", " stream))))
(write-string " }" stream))
;;; Interface
(defun emit (value stream)
"Emit a value to a stream."
(encode value stream))
(defun emit-to-string (value)
"Emit a value to string."
(with-output-to-string (stream)
(emit value stream)))
|
9641dc0a |
;;; Wrappers around cl-libyaml event interface with defaults and keyword args
|
85d0b811 |
|
9641dc0a |
(defun stream-start-event (event &key (encoding :utf8-encoding))
(stream-start-event-initialize event encoding))
(defun stream-end-event (event)
(stream-end-event-initialize event))
(defun document-start-event (event &key (version-directive (null-pointer))
|
6acdefbe |
(tag-directive-start (null-pointer))
(tag-directive-end (null-pointer))
(implicit nil))
|
9641dc0a |
(document-start-event-initialize event version-directive
|
6acdefbe |
tag-directive-start
tag-directive-end
implicit))
|
9641dc0a |
(defun document-end-event (event &key (implicit nil))
(document-end-event-initialize event implicit))
(defun sequence-start-event (event &key (anchor (null-pointer))
|
6acdefbe |
(tag (null-pointer))
(implicit nil)
(style :any-sequence-style))
|
9641dc0a |
(sequence-start-event-initialize event anchor tag implicit style))
(defun sequence-end-event (event)
(sequence-end-event-initialize event))
(defun mapping-start-event (event &key (anchor (null-pointer))
|
6acdefbe |
(tag (null-pointer))
(implicit nil)
(style :any-mapping-style))
|
9641dc0a |
(mapping-start-event-initialize event anchor tag implicit style))
(defun mapping-end-event (event)
(mapping-end-event-initialize event))
(defun scalar-event (event value length &key (anchor (null-pointer))
|
6acdefbe |
(tag (null-pointer))
(plain-implicit t)
(quoted-implicit t)
(style :plain-scalar-style))
|
9641dc0a |
(scalar-event-initialize event anchor tag value length
|
6acdefbe |
plain-implicit quoted-implicit style))
|
9641dc0a |
;;; Emitter macros and output functions
;;; When passing a foreign emitter object, it is also paired with a
;;; foreign event object.
(defun foreign-emitter (emitter) (car emitter))
(defun foreign-event (emitter) (cdr emitter))
(defmacro with-emitter-to-stream ((emitter-var output-stream) &rest body)
(let ((foreign-emitter (gensym "EMITTER"))
|
6acdefbe |
(foreign-event (gensym "EVENT")))
|
9641dc0a |
`(let* ((,foreign-emitter (allocate-emitter))
|
6acdefbe |
(,foreign-event (allocate-event))
(,emitter-var (cons ,foreign-emitter ,foreign-event))
(*write-handler-stream* ,output-stream))
|
9641dc0a |
(unwind-protect
|
6acdefbe |
(progn
(emitter-initialize ,foreign-emitter)
(set-output ,foreign-emitter *write-handler-callback* (null-pointer))
,@body)
(libyaml.event:event-delete ,foreign-event)
(libyaml.emitter:emitter-delete ,foreign-emitter)
(foreign-free ,foreign-event)
(foreign-free ,foreign-emitter)))))
|
9641dc0a |
(defmacro with-emitter-to-string ((emitter-var) &rest body)
(let ((str (gensym "STR")))
`(with-output-to-string (,str)
(with-emitter-to-stream (,emitter-var ,str)
|
6acdefbe |
,@body))))
|
942da252 |
|
9641dc0a |
(defmacro emit-stream ((emitter &key (encoding :utf8-encoding)) &body body)
(let ((emitter-value (gensym "EMITTER"))
|
6acdefbe |
(foreign-emitter (gensym "FOREIGN-EMITTER"))
(foreign-event (gensym "FOREIGN-EVENT")))
|
9641dc0a |
`(let* ((,emitter-value ,emitter)
|
6acdefbe |
(,foreign-emitter (foreign-emitter ,emitter-value))
(,foreign-event (foreign-event ,emitter-value)))
|
9641dc0a |
(stream-start-event ,foreign-event :encoding ,encoding)
(libyaml.emitter:emit ,foreign-emitter ,foreign-event)
,@body
(stream-end-event ,foreign-event)
(libyaml.emitter:emit ,foreign-emitter ,foreign-event))))
(defmacro emit-document ((emitter &rest rest
|
6acdefbe |
&key version-directive
tag-directive-start
tag-directive-end
(implicit nil)) &body body)
|
9641dc0a |
(declare (ignorable version-directive tag-directive-start
|
6acdefbe |
tag-directive-end implicit))
|
9641dc0a |
(let ((emitter-value (gensym "EMITTER"))
|
6acdefbe |
(foreign-emitter (gensym "FOREIGN-EMITTER"))
(foreign-event (gensym "FOREIGN-EVENT")))
|
9641dc0a |
`(let* ((,emitter-value ,emitter)
|
6acdefbe |
(,foreign-emitter (foreign-emitter ,emitter-value))
(,foreign-event (foreign-event ,emitter-value)))
|
9641dc0a |
(apply #'document-start-event ,foreign-event (list ,@rest))
(libyaml.emitter:emit ,foreign-emitter ,foreign-event)
,@body
(document-end-event ,foreign-event :implicit ,implicit)
(libyaml.emitter:emit ,foreign-emitter ,foreign-event))))
(defmacro emit-mapping ((emitter &rest rest &key anchor tag implicit style)
|
6acdefbe |
&body body)
|
9641dc0a |
(declare (ignorable anchor tag implicit style))
(let ((emitter-value (gensym "EMITTER"))
|
6acdefbe |
(foreign-emitter (gensym "FOREIGN-EMITTER"))
(foreign-event (gensym "FOREIGN-EVENT")))
|
9641dc0a |
`(let* ((,emitter-value ,emitter)
|
6acdefbe |
(,foreign-emitter (foreign-emitter ,emitter-value))
(,foreign-event (foreign-event ,emitter-value)))
|
9641dc0a |
(apply #'mapping-start-event ,foreign-event (list ,@rest))
(libyaml.emitter:emit ,foreign-emitter ,foreign-event)
,@body
(mapping-end-event ,foreign-event)
(libyaml.emitter:emit ,foreign-emitter ,foreign-event))))
(defmacro emit-sequence ((emitter &rest rest &key anchor tag implicit style)
|
6acdefbe |
&body body)
|
9641dc0a |
(declare (ignorable anchor tag implicit style))
(let ((emitter-value (gensym "EMITTER"))
|
6acdefbe |
(foreign-emitter (gensym "FOREIGN-EMITTER"))
(foreign-event (gensym "FOREIGN-EVENT")))
|
9641dc0a |
`(let* ((,emitter-value ,emitter)
|
6acdefbe |
(,foreign-emitter (foreign-emitter ,emitter-value))
(,foreign-event (foreign-event ,emitter-value)))
|
9641dc0a |
(apply #'sequence-start-event ,foreign-event (list ,@rest))
(libyaml.emitter:emit ,foreign-emitter ,foreign-event)
,@body
(sequence-end-event ,foreign-event)
(libyaml.emitter:emit ,foreign-emitter ,foreign-event))))
(defun emit-scalar (emitter value &rest rest &key anchor tag
|
6acdefbe |
plain-implicit
quoted-implicit
style)
|
9641dc0a |
(declare (ignorable anchor tag plain-implicit quoted-implicit style))
(let ((printed-value (print-scalar value)))
(apply #'scalar-event (foreign-event emitter)
|
6acdefbe |
printed-value (length printed-value) rest)
|
90e18416 |
(libyaml.emitter:emit (foreign-emitter emitter) (foreign-event emitter))))
|
9641dc0a |
(defgeneric print-scalar (scalar)
(:documentation "Convert a scalar object into its printed representation"))
(defmethod print-scalar ((scalar (eql 't)))
"true")
(defmethod print-scalar ((scalar (eql 'nil)))
"false")
(defmethod print-scalar ((scalar symbol))
(symbol-name scalar))
(defmethod print-scalar ((scalar string))
scalar)
(defmethod print-scalar ((scalar integer))
(princ-to-string scalar))
(defmethod print-scalar ((scalar single-float))
(let ((*read-default-float-format* 'single-float))
(princ-to-string scalar)))
(defmethod print-scalar ((scalar double-float))
(let ((*read-default-float-format* 'double-float))
(princ-to-string scalar)))
(defgeneric emit-object (emitter obj)
(:documentation "Emit YAML representation of obj"))
|
90e18416 |
(defmethod emit-object (emitter (obj symbol))
(emit-scalar emitter obj))
(defmethod emit-object (emitter (obj string))
(emit-scalar emitter obj))
(defmethod emit-object (emitter (obj integer))
(emit-scalar emitter obj))
(defmethod emit-object (emitter (obj float))
(emit-scalar emitter obj))
|