git.fiddlerwoaroof.com
src/parser.lisp
e98e9178
 (in-package :cl-user)
 (defpackage yaml.parser
   (:use :cl)
059c5c6d
   (:import-from :alexandria
                 :destructuring-case)
e98e9178
   (:import-from :libyaml.macros
                 :with-parser
                 :with-event)
23d3a77f
   (:export :parse-string
 	   :register-scalar-converter
 	   :register-sequence-converter
 	   :register-mapping-converter)
e98e9178
   (:documentation "The YAML parser."))
 (in-package :yaml.parser)
 
23d3a77f
 (defvar +scalar-converters+ (make-hash-table :test #'equalp))
 (defvar +sequence-converters+ (make-hash-table :test #'equalp))
 (defvar +mapping-converters+ (make-hash-table :test #'equalp))
 
 (defun scalar-converter (tag)
   (gethash tag +scalar-converters+))
 
 (defun convert-scalar (string tag &optional (style :plain-scalar-stype))
   (let ((converter (scalar-converter tag)))
     (if converter
 	(funcall converter string)
 	(yaml.scalar:parse-scalar string style))))
 
 (defun sequence-converter (tag)
   (gethash tag +sequence-converters+))
 
 (defun convert-sequence (list tag)
   (let ((converter (sequence-converter tag)))
     (if converter
 	(funcall converter list)
 	list)))
 
 (defun mapping-converter (tag)
   (gethash tag +mapping-converters+))
 
 (defun convert-mapping (hashtable tag)
   (let ((converter (mapping-converter tag)))
     (if converter
 	(funcall converter hashtable)
 	hashtable)))
 
e98e9178
 ;;; The parser
 
 (defun signal-reader-error (parser)
   (let ((message (libyaml.parser:error-message parser))
         (line (libyaml.parser:error-line parser))
         (column (libyaml.parser:error-column parser)))
     (error 'yaml.error:parsing-error
            :message message
            :line line
            :column column)))
 
 (defun parse-yaml (input)
   "Parse a YAML string, returning a list of tokens."
   (let ((output (make-array 0 :fill-pointer 0 :adjustable t)))
     (with-parser (parser input)
       (with-event (event)
         (loop do
           ;; Parse the next event, checking for errors
e792d5f3
           (let ((parsing-result (libyaml.parser:parse parser event)))
             (if parsing-result
                 ;; Decide what to do with the event
                 (let ((type (libyaml.event:event-type event)))
                   (flet ((add-to-output (data)
                            (vector-push-extend data output)))
                     (cond
                       ;; Stream events
                       ((eql type :stream-start-event)
                        ;; Do nothing
                        t)
                       ((eql type :stream-end-event)
                        (return-from parse-yaml output))
                       ;; Document events, push them to the output list
                       ((or (eql type :document-start-event)
                            (eql type :document-end-event))
                        (add-to-output (list type)))
                       ;; Alias and scalar event, push the type and data pair to
d009da3f
                       ;; the output list. Disabled since they are not supported.
                       #|
e792d5f3
                       ((eql type :alias-event)
                        (add-to-output
                         (cons type
                               (libyaml.event:event-alias-data event))))
d009da3f
                       |#
e792d5f3
                       ((eql type :scalar-event)
                        (add-to-output
                         (cons type
                               (libyaml.event:event-scalar-data event))))
                       ;; Sequence start and end events
                       ((eql type :sequence-start-event)
                        (add-to-output
                         (cons type
                               (libyaml.event:event-sequence-start-data event))))
                       ((eql type :sequence-end-event)
                        (add-to-output (list type)))
                       ;; Mapping start and end events
                       ((eql type :mapping-start-event)
                        (add-to-output
                         (cons type
                               (libyaml.event:event-mapping-start-data event))))
                       ((eql type :mapping-end-event)
d009da3f
                        (add-to-output (list type))))))
e792d5f3
                 ;; Signal an error
                 (signal-reader-error parser))))))))
e98e9178
 
 (defun parse-tokens (vector)
25882f04
   (let ((contexts (list (list :documents))))
7f59778c
     (loop for token across vector do
       (destructuring-case token
         ;; Documents
         ((:document-start-event)
          (push (list) contexts))
         ((:document-end-event)
          (let ((con (pop contexts)))
            (setf (first contexts)
                  (append (first contexts)
25882f04
                          con))))
7f59778c
         ;; Alias event
d009da3f
         ;; Disabled since it's not supported
         #|
7f59778c
         ((:alias-event &key anchor)
e792d5f3
          (declare (ignore anchor))
          t)
d009da3f
         |#
7f59778c
         ;; Scalar
23d3a77f
         ((:scalar-event &key anchor tag value length plain-implicit quoted-implicit style)
          (declare (ignore anchor length plain-implicit quoted-implicit))
7f59778c
          (setf (first contexts)
49f18a72
                (append (first contexts)
23d3a77f
                        (list (convert-scalar value tag style)))))
7f59778c
         ;; Sequence start event
23d3a77f
         ((:sequence-start-event &key anchor tag implicit style)
          (declare (ignore anchor implicit style))
          (push (list tag) contexts))
7f59778c
         ;; Mapping start event
23d3a77f
         ((:mapping-start-event &key anchor tag implicit style)
          (declare (ignore anchor implicit style))
          (push (list tag) contexts))
7f59778c
         ;; End events
         ((:sequence-end-event)
23d3a77f
          (destructuring-bind (tag &rest seq) (pop contexts)
7f59778c
            (setf (first contexts)
                  (append (first contexts)
23d3a77f
                          (list (convert-sequence seq tag))))))
7f59778c
         ((:mapping-end-event)
23d3a77f
          (destructuring-bind (tag &rest plist) (pop contexts)
7f59778c
            (setf (first contexts)
                  (append (first contexts)
23d3a77f
                          (list (convert-mapping
 				(alexandria:plist-hash-table plist :test #'equalp)
 				tag))))))
7f59778c
         ;; Do nothing
         ((t &rest rest)
71389615
          (declare (ignore rest)))))
7f59778c
     (first contexts)))
e98e9178
 
 ;;; The public interface
 
23d3a77f
 (defun register-scalar-converter (tag converter)
   (setf (gethash tag +scalar-converters+) converter))
 
 (defun register-sequence-converter (tag converter)
   (setf (gethash tag +sequence-converters+) converter))
 
 (defun register-mapping-converter (tag converter)
   (setf (gethash tag +mapping-converters+) converter))
 
e792d5f3
 (defun parse-string (yaml-string)
   (parse-tokens (parse-yaml yaml-string)))