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)))
|