git.fiddlerwoaroof.com
Raw Blame History
(in-package :cl-user)
(defpackage yaml.scalar
  (:use :cl)
  (:export :parse-scalar :*yaml-11-integers*)
  (:documentation "Parser for scalar values."))
(in-package :yaml.scalar)

;;; Constants

(defparameter +null+ nil
  "The NULL constant. Nil by default.")

(defparameter +false+ nil
  "The falsehood constant. Nil by default.")

;;; Regular expressions or lists of names

(defparameter +quoted-scalar-styles+
  (list :single-quoted-scalar-style :double-quoted-scalar-style))

(defparameter +null-names+
  (list "null" "Null" "NULL" "~"))

(defparameter +true-names+
  (list "true" "True" "TRUE"))

(defparameter +false-names+
  (list "false" "False" "FALSE"))

(defparameter +integer-scanner+
  (ppcre:create-scanner "^([-+]?[0-9]+)$"))

(defparameter +octal-integer-scanner+
  (ppcre:create-scanner "^0o([0-7]+)$"))

(defparameter +hex-integer-scanner+
  (ppcre:create-scanner "^0x([0-9a-fA-F]+)$"))

(defparameter +float-scanner+
  (ppcre:create-scanner
   "^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$"))

(defparameter +nan-names+
  (list ".nan" ".NaN" ".NAN"))

(defparameter +positive-infinity-scanner+
  (ppcre:create-scanner "^[+]?(\\.inf|\\.Inf|\\.INF)$"))

(defparameter +negative-infinity-scanner+
  (ppcre:create-scanner "^-(\\.inf|\\.Inf|\\.INF)$"))

;;; The actual parser
(defvar *yaml-11-integers* nil)

(defun parse-yaml-integer (string)
  (if (and *yaml-11-integers*
           (eql #\0 (elt string 0)))
      (if (every (lambda (it)
                   (member it '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)))
                 string)
          (parse-integer string :radix 8)
          string)
      (parse-integer string)))

(defun parse-scalar (string &optional (style :plain-scalar-style))
  "Parse a YAML scalar string into a Lisp scalar value."
  (cond
    ;; Quoted string
    ((member style +quoted-scalar-styles+)
     string)
    ;; Null
    ((member string +null-names+ :test #'equal)
     +null+)
    ;; Truth and falsehood
    ((member string +true-names+ :test #'equal)
     t)
    ((member string +false-names+ :test #'equal)
     +false+)
    ;; Integers
    ((ppcre:scan +integer-scanner+ string)
     (parse-yaml-integer string))
    ((ppcre:scan +octal-integer-scanner+ string)
     (parse-integer (subseq string 2) :radix 8))
    ((ppcre:scan +hex-integer-scanner+ string)
     (parse-integer (subseq string 2) :radix 16))
    ;; Floating-point numbers
    ((ppcre:scan +float-scanner+ string)
     (parse-number:parse-real-number string))
    ;; Special floats
    ((member string +nan-names+ :test #'equal)
     (yaml.float:not-a-number))
    ((ppcre:scan +positive-infinity-scanner+ string)
     (yaml.float:positive-infinity))
    ((ppcre:scan +negative-infinity-scanner+ string)
     (yaml.float:negative-infinity))
    ;; Just a string
    (t
     string)))