git.fiddlerwoaroof.com
src/emitter.lisp
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))