(cl:in-package #:sod)
+;;;--------------------------------------------------------------------------
+;;; Conversion utilities.
+
+(defun string-to-symbol
+ (string &key (package *package*) (swap-case t) (swap-hyphen t))
+ "Convert STRING to a symbol in PACKAGE.
+
+ Parse off a `PACKAGE:' prefix from STRING if necessary, to identify the
+ package; PACKAGE is used if there isn't a prefix. A doubled colon allows
+ access to internal symbols, and will intern if necessary. Note that
+ escape characters are /not/ processed; don't put colons in package names
+ if you want to use them from SOD property sets.
+
+ The portions of the string are modified by `frob-identifier'; the
+ arguments SWAP-CASE and SWAP-HYPHEN are passed to `frob-identifier' to
+ control this process."
+
+ (let* ((length (length string))
+ (colon (position #\: string)))
+ (multiple-value-bind (start internalp)
+ (cond ((not colon) (values 0 t))
+ ((and (< (1+ colon) length)
+ (char= (char string (1+ colon)) #\:))
+ (values (+ colon 2) t))
+ (t
+ (values (1+ colon) nil)))
+ (when colon
+ (let* ((package-name (if (zerop colon) "KEYWORD"
+ (frob-identifier (subseq string 0 colon)
+ :swap-case swap-case
+ :swap-hyphen swap-hyphen)))
+ (found (find-package package-name)))
+ (unless found
+ (error "Unknown package `~A'" package-name))
+ (setf package found)))
+ (let ((name (frob-identifier (subseq string start)
+ :swap-case swap-case
+ :swap-hyphen swap-hyphen)))
+ (multiple-value-bind (symbol status)
+ (funcall (if internalp #'intern #'find-symbol) name package)
+ (cond ((or internalp (eq status :external))
+ symbol)
+ ((not status)
+ (error "Symbol `~A' not found in package `~A'"
+ name (package-name package)))
+ (t
+ (error "Symbol `~A' not external in package `~A'"
+ name (package-name package)))))))))
+
+(let ((truish '("true" "t" "yes" "verily"))
+ (falsish '("false" "nil" "no" "nowise")))
+ (defun truishp (string)
+ "Convert STRING to a boolean."
+ (cond ((member string truish :test #'string=) t)
+ ((member string falsish :test #'string=) nil)
+ (t (error "Unrecognized boolean value `~A'" string)))))
+
;;;--------------------------------------------------------------------------
;;; Property representation.
((value symbol) (type (eql :symbol)) (wanted (eql :id)))
(frob-identifier (symbol-name value)))
+;;; Boolean.
+
+(defmethod coerce-property-value
+ ((value symbol) (type (eql :symbol)) (wanted (eql :boolean)))
+ value)
+
+(defmethod coerce-property-value
+ ((value string) (type (eql :id)) (wanted (eql :boolean)))
+ (truishp value))
+
+(defmethod coerce-property-value
+ ((value integer) (type (eql :int)) (wanted (eql :boolean)))
+ (not (zerop value)))
+
;;; Types.
(defmethod coerce-property-value