Compact syntax parsing fixes
[cxml-rng.git] / types.lisp
blob26f89fa87843414743e5aa62d3762249cf5c8c99
1 ;;; -*- show-trailing-whitespace: t; indent-tabs: nil -*-
2 ;;;
3 ;;; Copyright (c) 2007 David Lichteblau. All rights reserved.
5 ;;; Redistribution and use in source and binary forms, with or without
6 ;;; modification, are permitted provided that the following conditions
7 ;;; are met:
8 ;;;
9 ;;; * Redistributions of source code must retain the above copyright
10 ;;; notice, this list of conditions and the following disclaimer.
11 ;;;
12 ;;; * Redistributions in binary form must reproduce the above
13 ;;; copyright notice, this list of conditions and the following
14 ;;; disclaimer in the documentation and/or other materials
15 ;;; provided with the distribution.
16 ;;;
17 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
18 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 (in-package :cxml-types)
31 (defstruct (param (:constructor make-param (name value)))
32 "@short{A named data type parameter.}
34 (With the XSD type library, parameters are known as restricting facets.)
35 @see-constructor{make-param}
36 @see{find-type}
37 @see{cxml-rng:pattern-params}
38 @see{cxml-rng:data}
39 @see-slot{param-name}
40 @see-slot{param-value}"
41 name
42 value)
44 (setf (documentation 'make-param 'function)
45 "@arg[name]{parameter name, a string}
46 @arg[value]{parameter value, a string}
47 @return{a @class{param}}
48 Create a data type parameter.
49 @see{param-name}
50 @see{param-value}")
52 (setf (documentation 'param-name 'function)
53 "@arg[instance]{an instance of @class{param}}
54 @return{a string}
55 The data type parameter's name.
56 @see{param-value}")
58 (setf (documentation 'param-value 'function)
59 "@arg[instance]{an instance of @class{param}}
60 @return{a string}
61 The data type parameter's value.
62 @see{param-name}")
64 (defclass data-type () ()
65 (:documentation
66 "@short{The abstract superclass of all types.}
68 Each type belongs to a datatype library, named by a keyword. In each
69 library, the types are named by strings.
71 @see-constructor{find-type}
72 @see-slot{type-name}
73 @see-slot{type-library}
74 @see-slot{type-context-dependent-p}
75 @see-slot{type-id-type}
76 @see{parse}
77 @see{equal-using-type}
78 @see{lessp-using-type}
79 @see{validp}"))
81 (defgeneric find-type (library name params)
82 (:documentation
83 "@arg[library]{datatype library, a keyword symbol}
84 @arg[name]{the type's name, a string}
85 @arg[params]{type parameters, a list of @class{param} instances}
86 @return{an instance of @class{data-type}, or @code{nil}}
87 @short{Look up the type named @em{name} in datatype library @em{library}.}
89 Additional parameters (knows as restricting facets in XSD) can be passed
90 to specify or restrict the type for the purposes of @fun{validp}.
92 Return a type instance for this type and the additional parameters,
93 @code{nil} if the type does not exist, or
94 @code{:error} if the type exists, but the specified parameters are not
95 valid for that type.
97 @see{data-type}"))
99 (defgeneric type-library (type)
100 (:documentation
101 "@arg[type]{an instance of @class{data-type}}
102 @return{library name, a keyword}
103 @short{Return the name of the library this type belongs to.}
105 @see{type-name}
106 @see{type-context-dependent-p}
107 @see{type-id-type}"))
109 (defgeneric type-name (type)
110 (:documentation
111 "@arg[type]{an instance of @class{data-type}}
112 @return{type name, a string}
113 @short{Return the name this type has within its library.}
115 @see{type-library}
116 @see{type-context-dependent-p}
117 @see{type-id-type}"))
119 (defmethod find-type ((library t) name params)
120 (declare (ignore name params))
121 nil)
123 (defgeneric type-context-dependent-p (type)
124 (:documentation
125 "@arg[type]{an instance of @class{data-type}}
126 @return{a boolean}
127 @short{Return true if parsing and validation of values by this type
128 depends on the validation context.}
130 In this case, the optional @code{context} argument to @fun{parse} and
131 @fun{validp} is required, and an error will be signalled if it is missing.
133 @see{validation-context}
134 @see{type-name}
135 @see{type-library}
136 @see{type-context-dependent-p}
137 @see{type-id-type}"))
139 (defmethod type-context-dependent-p ((type data-type))
140 nil)
142 (defgeneric type-id-type (type)
143 (:documentation
144 "@arg[type]{an instance of @class{data-type}}
145 @return{one of @code{nil}, @code{:id}, @code{:idref}, or @code{:idrefs}}
146 @short{Returns the @em{ID-type} of @code{type}.}
148 The symbols @code{nil}, @code{:id}, @code{:idref}, or @code{:idrefs}
149 represent the ID-types @em{null}, @em{ID}, @em{IDREF}, and @em{IDREFS},
150 respectively, as defined by
151 @a[http://relaxng.org/compatibility-20011203.html]{
152 RELAX NG DTD Compatibility}.
154 @see{type-name}
155 @see{type-library}
156 @see{type-context-dependent-p}"))
158 (defmethod type-id-type ((type data-type))
159 nil)
161 (defgeneric equal-using-type (type u v)
162 (:documentation
163 "@arg[type]{an instance of @class{data-type}}
164 @arg[u]{a parsed value as returned by @fun{parse}}
165 @arg[v]{a parsed value as returned by @fun{parse}}
166 @return{a boolean}
167 @short{Compare the @emph{values} @code{u} and @code{v} using a
168 data-type-dependent equality function.}
170 @see{validp}"))
172 (defgeneric parse (type e &optional context)
173 (:documentation
174 "@arg[type]{an instance of @class{data-type}}
175 @arg[e]{a string}
176 @arg[context]{an instance of @class{validation-context}}
177 @return{an object}
178 @short{Parse string @code{e} and return a representation of its value
179 as defined by the data type.}
181 The @code{context} argument is required if @fun{type-context-dependent-p}
182 is true for @code{type}, and will be ignored otherwise.
184 @see{equal-using-type}
185 @see{validp}"))
187 (defgeneric validp (type e &optional context)
188 (:documentation
189 "@arg[type]{an instance of @class{data-type}}
190 @arg[e]{a string}
191 @arg[context]{an instance of @class{validation-context}}
192 @return{a boolean}
193 @short{Determine whether a string is a valid lexical representation
194 for a type.}
196 The @code{context} argument is required if @fun{type-context-dependent-p}
197 is true for @code{type}, and will be ignored otherwise.
199 @see{parse}
200 @see{equal-using-type}"))
203 ;;; Validation context
205 (defclass validation-context () ()
206 (:documentation
207 "@short{This abstract class defines a protocol allowing data types
208 to query the XML parser about its current state.}
210 Some types are context dependent, as indicated by
211 @fun{type-context-dependent-p}. Those types need access to state
212 computed by the XML parser implicitly, like namespace bindings or
213 the Base URI.
215 User-defined subclasses must implement methods
216 for the functions @fun{context-find-namespace-binding} and
217 @fun{context-find-unparsed-entity}.
219 Two pre-defined validation context implementations are
220 provided, one for use with SAX, the other based on Klacks."))
222 (defgeneric context-find-namespace-binding (context prefix)
223 (:documentation
224 "@arg[context]{an instance of @class{validation-context}}
225 @arg[prefix]{name prefix, a string}
226 @return{the namespace URI as a string, or NIL}
227 @short{This function resolves a namespace prefix to a namespace URI in the
228 current context.}
229 All currently declared namespaces
230 are taken into account, including those declared directly on the
231 current element."))
233 (defgeneric context-find-unparsed-entity (context name)
234 (:documentation
235 "@arg[context]{an instance of @class{validation-context}}
236 @arg[name]{entity name, a string}
237 @return{@code{nil}, or a list of public id, system id, and notation name}
238 This function looks for an unparsed entity in the current context."))
240 (defclass klacks-validation-context (validation-context)
241 ((source :initarg :source :accessor context-source))
242 (:documentation
243 "A validation-context implementation that queries
244 a klacks source for information about the parser's current state.
245 @see-constructor{make-klacks-validation-context}"))
247 (defun make-klacks-validation-context (source)
248 "@arg[source]{a @a[http://common-lisp.net/project/cxml/klacks.html]{
249 klacks source}}
250 @return{a @class{klacks-validation-context}}
251 Create a validation-context that will query the given klacks source for
252 the current parser context."
253 (make-instance 'klacks-validation-context :source source))
255 (defmethod context-find-namespace-binding
256 ((context klacks-validation-context) prefix)
257 (klacks:find-namespace-binding prefix (context-source context)))
259 ;; zzz nicht schoen.
260 (defmethod context-find-unparsed-entity
261 ((context klacks-validation-context) name)
262 (or (dolist (x (slot-value (context-source context)
263 'cxml::external-declarations))
264 (when (and (eq (car x) 'sax:unparsed-entity-declaration)
265 (equal (cadr x) name))
266 (return t)))
267 (dolist (x (slot-value (context-source context)
268 'cxml::internal-declarations))
269 (when (and (eq (car x) 'sax:unparsed-entity-declaration)
270 (equal (cadr x) name))
271 (return t)))))
273 (defclass sax-validation-context-mixin (validation-context)
274 ((stack :initform nil :accessor context-stack)
275 (unparsed-entities :initform (make-hash-table :test 'equal)
276 :accessor unparsed-entities))
277 (:documentation
278 "@short{A class that implements validation-context as a mixin for
279 user-defined SAX handler classes.}
281 The mixin will record namespace information
282 automatically, and the user's SAX handler can simply be passed as a
283 validation context to data type functions."))
285 (defmethod sax:start-prefix-mapping :after
286 ((handler sax-validation-context-mixin) prefix uri)
287 (push (cons prefix uri) (context-stack handler)))
289 (defmethod sax:end-prefix-mapping :after
290 ((handler sax-validation-context-mixin) prefix)
291 (setf (context-stack handler)
292 (remove prefix
293 (context-stack handler)
294 :count 1
295 :key #'car
296 :test #'equal)))
298 (defmethod sax:unparsed-entity-declaration
299 ((context sax-validation-context-mixin)
300 name public-id system-id notation-name)
301 (setf (gethash name (unparsed-entities context))
302 (list public-id system-id notation-name)))
304 (defmethod context-find-namespace-binding
305 ((context sax-validation-context-mixin) prefix)
306 (cdr (assoc prefix (context-stack context) :test #'equal)))
308 (defmethod context-find-unparsed-entity
309 ((context sax-validation-context-mixin) name)
310 (gethash name (unparsed-entities context)))
313 ;;; Relax NG built-in type library
315 (defclass rng-type (data-type) ()
316 (:documentation
317 "@short{The class of Relax NG built-in types.}
318 Relax NG defines two built-in data type: string and token.
320 The Relax NG type library is named @code{:||}."))
322 (defmethod print-object ((object rng-type) stream)
323 (print-unreadable-object (object stream :type t :identity nil)))
325 (defclass string-type (rng-type) ()
326 (:documentation
327 "@short{The Relax NG 'string' type.}
328 This data type allows arbitrary strings and interprets them as-is.
330 For this type, @fun{parse} will return any string unchanged, and
331 @fun{equal-using-type} compares strings using @code{equal}."))
333 (defclass token-type (rng-type) ()
334 (:documentation
335 "@short{The Relax NG 'token' type.}
336 This data type allows arbitrary strings and normalizes all whitespaces.
338 For this type, @fun{parse} will return the string with leading and
339 trailing whitespace removed, and remaining sequences of spaces
340 compressed down to one space character each.
342 A method for @fun{equal-using-type} compares strings using @code{equal}."))
344 (defmethod type-library ((type rng-type))
345 :||)
347 (defvar *string-data-type* (make-instance 'string-type))
348 (defvar *token-data-type* (make-instance 'token-type))
350 (defmethod find-type ((library (eql :||)) name params)
351 (cond
352 ((eq name :probe) t)
353 (params :error)
354 ((equal name "string") *string-data-type*)
355 ((equal name "token") *token-data-type*)
356 (t nil)))
358 (defmethod equal-using-type ((type rng-type) u v)
359 (equal u v))
361 (defmethod validp ((type rng-type) e &optional context)
362 (declare (ignore e context))
365 (defmethod type-name ((type string-type)) "string")
366 (defmethod type-name ((type token-type)) "token")
368 (defmethod parse ((type string-type) e &optional context)
369 (declare (ignore context))
372 (defmethod parse ((type token-type) e &optional context)
373 (declare (ignore context))
374 (normalize-whitespace e))
376 (eval-when (:compile-toplevel :load-toplevel :execute)
377 (defparameter *whitespace*
378 (format nil "~C~C~C~C"
379 (code-char 9)
380 (code-char 32)
381 (code-char 13)
382 (code-char 10))))
384 (defun normalize-whitespace (str)
385 (cl-ppcre:regex-replace-all #.(format nil "[~A]+" *whitespace*)
386 (string-trim *whitespace* str)
387 " "))
389 (defun replace-whitespace (str)
390 (cl-ppcre:regex-replace-all #.(format nil "[~A]" *whitespace*)
392 " "))
395 ;;; DTD compatibility types
397 (defclass dtd-compatibility-type (data-type)
398 ((chained-type :accessor chained-type))
399 (:documentation
400 "@short{The class of DTD Compatibility data types.}
402 This library contains three types: ID, IDREF, and IDREFS.
404 This type library is named
405 @code{:|http://relaxng.org/ns/compatibility/datatypes/1.0|}."))
407 (defmethod print-object ((object dtd-compatibility-type) stream)
408 (print-unreadable-object (object stream :type t :identity nil)))
410 (defclass id-type (dtd-compatibility-type) ()
411 (:documentation
412 "@short{The DTD compatibility 'ID' type.}
414 For this type, @fun{parse} will return the string with leading and
415 trailing whitespace removed.
417 The resulting value must be an NCName.
419 The ID-type of this data type is 'ID', ensuring that each value is
420 only used for one element in a document.
422 @see{xsd-id-type}"))
424 (defclass idref-type (dtd-compatibility-type) ()
425 (:documentation
426 "@short{The DTD compatibility 'IDREF' type.}
428 For this type, @fun{parse} will return the string with leading and
429 trailing whitespace removed.
431 The resulting value must be an NCName.
433 The ID-type of this data type is 'IDREF', ensuring that the value
434 referenced must be declared as the ID of an element in the document.
436 @see{xsd-idref-type}"))
438 (defclass idrefs-type (dtd-compatibility-type) ()
439 (:documentation
440 "@short{The DTD compatibility 'IDREFS' type.}
442 Strings are valid for this data type they contain a whitespace-separated
443 list of one or more NCNames. @fun{parse} will return a list of these
444 substrings.
446 The ID-type of this data type is 'IDREFS', ensuring that each value
447 referenced must be declared as the ID of an element in the document.
449 @see{xsd-idrefs-type}"))
451 ;; die Implementation dieser Typen deligieren wir einfach mal an die
452 ;; entsprechenden XSD-Typen.
453 (defmethod initialize-instance :after ((instance id-type) &key)
454 (setf (chained-type instance)
455 (or (find-type :|http://www.w3.org/2001/XMLSchema-datatypes| "ID" nil)
456 (error "oops"))))
458 (defmethod initialize-instance :after ((instance idref-type) &key)
459 (setf (chained-type instance)
460 (or (find-type :|http://www.w3.org/2001/XMLSchema-datatypes| "IDREF" nil)
461 (error "oops"))))
463 (defmethod initialize-instance :after ((instance idrefs-type) &key)
464 (setf (chained-type instance)
465 (or (find-type :|http://www.w3.org/2001/XMLSchema-datatypes| "IDREFS" nil)
466 (error "oops"))))
468 (defmethod type-library ((type dtd-compatibility-type))
469 :|http://relaxng.org/ns/compatibility/datatypes/1.0|)
471 (defmethod type-name ((type id-type)) "ID")
472 (defmethod type-name ((type idref-type)) "IDREF")
473 (defmethod type-name ((type idrefs-type)) "IDREFS")
475 ;; default values set below
476 (declaim (special *id-type*))
477 (declaim (special *idref-type*))
478 (declaim (special *idrefs-type*))
480 (defmethod find-type
481 ((library (eql :|http://relaxng.org/ns/compatibility/datatypes/1.0|||))
482 name params)
483 (cond
484 ((eq name :probe) t)
485 (params :error)
486 ((equal name "ID") *id-type*)
487 ((equal name "IDREF") *idref-type*)
488 ((equal name "IDREFS") *idrefs-type*)
489 (t nil)))
491 (defmethod validp ((type dtd-compatibility-type) e &optional context)
492 (validp (chained-type type) e context))
494 (defmethod parse ((type dtd-compatibility-type) e &optional context)
495 (parse (chained-type type) e context))
497 (defmethod type-id-type ((type dtd-compatibility-type))
498 (type-id-type (chained-type type)))
501 ;;; XML Schema Part 2: Datatypes Second Edition
503 (defparameter *xsd-types* (make-hash-table :test 'equal))
505 (defmacro defxsd
506 ((class-name type-name) (&rest supers) (&rest slots) &rest args)
507 `(progn
508 (setf (gethash ,type-name *xsd-types*) ',class-name)
509 (defclass ,class-name ,supers
510 ((type-name :initform ,type-name
511 :reader type-name
512 :allocation :class)
513 ,@slots)
514 ,@args)))
516 (defgeneric patterns (data-type)
517 (:documentation
518 "@arg[data-type]{a subtype of @class{xsd-type}}
519 @return{a list of strings}
520 This slot reader returns a list of the type's
521 @a[http://www.w3.org/TR/xmlschema-2/#rf-pattern]{pattern facets}."))
523 (defmethod (setf patterns) :after (newval data-type)
524 (slot-makunbound data-type 'compiled-patterns))
526 (defclass xsd-type (data-type)
527 ((patterns :initform nil :accessor patterns)
528 (compiled-patterns :accessor compiled-patterns))
529 (:documentation
530 "@short{The class of XML Schema built-in types.}
532 Subclasses of xsd-type provide the built-in types of
533 @a[http://www.w3.org/TR/xmlschema-2/]{
534 XML Schema Part 2: Datatypes Second Edition}
535 as specified in @a[http://relaxng.org/xsd-20010907.html]{Guidelines for
536 using W3C XML Schema Datatypes with RELAX NG}.
538 The XSD type library
539 is named @code{:|http://www.w3.org/2001/XMLSchema-datatypes|}.
541 @b{Parameters.} All XSD types accept regular expressions restricting
542 the set of strings accepted by the type. The pattern parameter is
543 called @code{\"pattern\"}. This parameter can be repeated to specify
544 multiple regular expressions that must all match the data.
545 As an initarg, specify @code{:pattern} with a list of regular expressions
546 as an argument.
548 @see-slot{patterns}"))
550 (defmethod initialize-instance :after ((instance xsd-type) &key patterns)
551 (setf (patterns instance) (append (patterns instance) patterns)))
553 (defmethod print-object ((object xsd-type) stream)
554 (print-unreadable-object (object stream :type t :identity nil)
555 (describe-facets object stream)))
557 (defgeneric describe-facets (object stream)
558 (:method-combination progn))
560 (defmethod describe-facets progn ((object xsd-type) stream)
561 (format stream "~{ :pattern ~A~}" (patterns object)))
563 (defmethod type-library ((type xsd-type))
564 :|http://www.w3.org/2001/XMLSchema-datatypes|)
566 (defun zip (keys values)
567 (loop for key in keys for value in values collect key collect value))
569 (defgeneric parse-parameter (class-name type-name param-name value))
571 (defun parse-parameters (type-class params)
572 (let ((patterns '())
573 (args '()))
574 (dolist (param params (values t patterns args))
575 (let ((name (param-name param))
576 (value (param-value param)))
577 (if (equal name "pattern")
578 (push value patterns)
579 (multiple-value-bind (key required-class)
580 (case (find-symbol (param-name param) :keyword)
581 (:|length| (values :exact-length 'length-mixin))
582 (:|maxLength| (values :max-length 'length-mixin))
583 (:|minLength| (values :min-length 'length-mixin))
584 (:|minInclusive| (values :min-inclusive 'ordering-mixin))
585 (:|maxInclusive| (values :max-inclusive 'ordering-mixin))
586 (:|minExclusive| (values :min-exclusive 'ordering-mixin))
587 (:|maxExclusive| (values :max-exclusive 'ordering-mixin))
588 (:|totalDigits| (values :total-digits 'decimal-type))
589 (:|fractionDigits| (values :fraction-digits 'decimal-type))
590 (t (return nil)))
591 (unless (subtypep type-class required-class)
592 (return nil))
593 (when (loop
594 for (k nil) on args by #'cddr
595 thereis (eq key k))
596 (return nil))
597 (push (parse-parameter required-class
598 type-class
600 (normalize-whitespace value))
601 args)
602 (push key args)))))))
604 (defmethod find-type
605 ((library (eql :|http://www.w3.org/2001/XMLSchema-datatypes|)) name params)
606 (if (eq name :probe)
608 (let ((class (gethash name *xsd-types*)))
609 (if class
610 (multiple-value-bind (ok patterns other-args)
611 (parse-parameters class params)
612 (if ok
613 (apply #'make-instance
614 class
615 :patterns patterns
616 other-args)
617 :error))
618 nil))))
620 (defgeneric parse/xsd (type e context))
622 (defgeneric validp/xsd (type v context)
623 (:method-combination and))
625 ;; make CLOS happy:
626 (defmethod validp/xsd and ((type xsd-type) v context)
627 (declare (ignore v context))
630 (defmethod validp ((type xsd-type) e &optional context)
631 (not (eq :error (parse/xsd type e context))))
633 (defmethod parse ((type xsd-type) e &optional context)
634 (let ((result (parse/xsd type e context)))
635 (when (eq result :error)
636 (error "not valid for data type ~A: ~S" type e))
637 result))
639 ;; Handle the whiteSpace "facet" before the subclass sees it.
640 ;; If parsing succeded, check other facets by asking validp/xsd.
641 (defmethod parse/xsd :around ((type xsd-type) e context)
642 (setf e (munge-whitespace type e))
643 (unless (slot-boundp type 'compiled-patterns)
644 (setf (compiled-patterns type)
645 (mapcar #'pattern-scanner (patterns type))))
646 (if (every (lambda (pattern)
647 (cl-ppcre:all-matches pattern e))
648 (compiled-patterns type))
649 (let ((result (call-next-method type e context)))
650 (if (or (eq result :error) (validp/xsd type result context))
651 result
652 :error))
653 :error))
655 (defgeneric munge-whitespace (type e))
657 (defmethod munge-whitespace ((type xsd-type) e)
658 (normalize-whitespace e))
661 ;;; ordering-mixin
663 (defgeneric min-exclusive (data-type)
664 (:documentation
665 "@arg[data-type]{an ordered data type}
666 @return{an integer, or @code{nil}}
667 This slot reader returns the type's
668 @a[http://www.w3.org/TR/xmlschema-2/#rf-minExclusive]{minExclusive facet},
669 or @code{nil} if none was specified.
670 @see{max-exclusive}
671 @see{min-inclusive}
672 @see{max-inclusive}"))
674 (defgeneric max-exclusive (data-type)
675 (:documentation
676 "@arg[data-type]{an ordered data type}
677 @return{an integer, or @code{nil}}
678 This slot reader returns the type's
679 @a[http://www.w3.org/TR/xmlschema-2/#rf-maxExclusive]{maxExclusive facet},
680 or @code{nil} if none was specified.
681 @see{min-exclusive}
682 @see{min-inclusive}
683 @see{max-inclusive}"))
685 (defgeneric min-inclusive (data-type)
686 (:documentation
687 "@arg[data-type]{an ordered data type}
688 @return{an integer, or @code{nil}}
689 This slot reader returns the type's
690 @a[http://www.w3.org/TR/xmlschema-2/#rf-minInclusive]{minInclusive facet},
691 or @code{nil} if none was specified.
692 @see{min-exclusive}
693 @see{max-exclusive}
694 @see{max-inclusive}"))
696 (defgeneric max-inclusive (data-type)
697 (:documentation
698 "@arg[data-type]{an ordered data type}
699 @return{an integer, or @code{nil}}
700 This slot reader returns the type's
701 @a[http://www.w3.org/TR/xmlschema-2/#rf-maxInclusive]{maxInclusive facet},
702 or @code{nil} if none was specified.
703 @see{min-exclusive}
704 @see{max-exclusive}
705 @see{min-inclusive}"))
707 (defclass ordering-mixin ()
708 ((min-exclusive :initform nil
709 :initarg :min-exclusive
710 :accessor min-exclusive)
711 (max-exclusive :initform nil
712 :initarg :max-exclusive
713 :accessor max-exclusive)
714 (min-inclusive :initform nil
715 :initarg :min-inclusive
716 :accessor min-inclusive)
717 (max-inclusive :initform nil
718 :initarg :max-inclusive
719 :accessor max-inclusive)))
721 (defmethod describe-facets progn ((object ordering-mixin) stream)
722 (dolist (slot '(min-exclusive max-exclusive min-inclusive max-inclusive))
723 (let ((value (slot-value object slot)))
724 (when value
725 (format stream " ~A ~A"
726 (intern (symbol-name slot) :keyword)
727 value)))))
729 (defmethod parse-parameter
730 ((class-name (eql 'ordering-mixin)) type-name (param t) value)
731 (parse (make-instance type-name) value nil))
733 (defgeneric lessp-using-type (type u v)
734 (:documentation
735 "@arg[type]{an ordered @class{data-type}}
736 @arg[u]{a parsed value as returned by @fun{parse}}
737 @arg[v]{a parsed value as returned by @fun{parse}}
738 @return{a boolean}
739 @short{Compare the @emph{values} @code{u} and @code{v} using a
740 data-type-dependent partial ordering.}
742 A method for this function is provided only by types that have a
743 natural partial ordering.
745 @see{equal-using-type}"))
747 (defun <-using-type (type u v)
748 (lessp-using-type type u v))
750 (defun <=-using-type (type u v)
751 (or (lessp-using-type type u v) (equal-using-type type u v)))
753 ;; it's only a partial ordering, so in general this is not the opposite of <=
754 (defun >-using-type (type u v)
755 (lessp-using-type type v u))
757 ;; it's only a partial ordering, so in general this is not the opposite of <
758 (defun >=-using-type (type u v)
759 (or (lessp-using-type type v u) (equal-using-type type v u)))
761 (defmethod validp/xsd and ((type ordering-mixin) v context)
762 (declare (ignore context))
763 (with-slots (min-exclusive max-exclusive min-inclusive max-inclusive) type
764 (and (or (null min-exclusive) (>-using-type type v min-exclusive))
765 (or (null max-exclusive) (<-using-type type v max-exclusive))
766 (or (null min-inclusive) (>=-using-type type v min-inclusive))
767 (or (null max-inclusive) (<=-using-type type v max-inclusive)))))
770 ;;; length-mixin
772 (defgeneric exact-length (data-type)
773 (:documentation
774 "@arg[data-type]{a data type supporting restrictions on value lengths}
775 @return{an integer, or @code{nil}}
776 This slot reader returns the type's
777 @a[http://www.w3.org/TR/xmlschema-2/#rf-length]{length facet},
778 or @code{nil} if none was specified.
779 @see{min-length}
780 @see{max-length}"))
782 (defgeneric min-length (data-type)
783 (:documentation
784 "@arg[data-type]{a data type supporting restrictions on value lengths}
785 @return{an integer, or @code{nil}}
786 This slot reader returns the type's
787 @a[http://www.w3.org/TR/xmlschema-2/#rf-minLength]{minLength facet},
788 or @code{nil} if none was specified.
789 @see{exact-length}
790 @see{max-length}"))
792 (defgeneric max-length (data-type)
793 (:documentation
794 "@arg[data-type]{a data type supporting restrictions on value lengths}
795 @return{an integer, or @code{nil}}
796 This slot reader returns the type's
797 @a[http://www.w3.org/TR/xmlschema-2/#rf-maxLength]{maxLength facet},
798 or @code{nil} if none was specified.
799 @see{exact-length}
800 @see{min-length}"))
802 (defclass length-mixin ()
803 ((exact-length :initform nil :initarg :exact-length :accessor exact-length)
804 (min-length :initform nil :initarg :min-length :accessor min-length)
805 (max-length :initform nil :initarg :max-length :accessor max-length)))
807 (defmethod describe-facets progn ((object length-mixin) stream)
808 (dolist (slot '(exact-length min-length max-length))
809 (let ((value (slot-value object slot)))
810 (when value
811 (format stream " ~A ~A"
812 (intern (symbol-name slot) :keyword)
813 value)))))
815 (defmethod parse-parameter
816 ((class-name (eql 'length-mixin)) (type-name t) (param t) value)
817 (parse (make-instance 'non-negative-integer-type) value nil))
819 ;; extra-hack fuer die "Laenge" eines QName...
820 (defgeneric length-using-type (type u))
821 (defmethod length-using-type ((type length-mixin) e) (length e))
823 (defmethod validp/xsd and ((type length-mixin) v context)
824 (declare (ignore context))
825 (with-slots (exact-length min-length max-length) type
826 (or (not (or exact-length min-length max-length))
827 (let ((l (length-using-type type v)))
828 (and (or (null exact-length) (eql l exact-length))
829 (or (null min-length) (>= l min-length))
830 (or (null max-length) (<= l max-length)))))))
833 ;;; enumeration-type
835 (defclass enumeration-type (xsd-type length-mixin)
836 ((word-type :reader word-type)))
838 (defmethod initialize-instance :after ((type enumeration-type) &key)
839 (setf (min-length type) (max* 1 (min-length type))))
841 (defmethod parse/xsd ((type enumeration-type) e context)
842 (let ((wt (word-type type)))
843 (loop
844 for word in (cl-ppcre:split " " e)
845 for v = (parse wt word context)
846 collect v
847 when (eq v :error) do (return :error))))
851 ;;;; Primitive types
853 ;;; duration
855 (defxsd (duration-type "duration") (xsd-type ordering-mixin)
857 (:documentation
858 "@short{The duration data type, representing a duration of time.}
860 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
861 the @a[http://www.w3.org/TR/xmlschema-2/#duration]{specification}.
863 @b{Implementation.} This type returns lists of the form
864 @code{(years months days hours minutes seconds)}. Each
865 value can be @code{nil} or a number. All values are integers
866 except for @code{seconds}, which is a real.
868 @b{Example.} @code{P1Y2M3DT10H30M}
869 maps to @code{(1 2 3 10 30 nil)}
871 @b{Parameters.} This type is ordered and allows the parameters
872 @slot{max-inclusive}, @slot{min-inclusive},
873 @slot{max-exclusive}, and @slot{min-exclusive}."))
875 (defmethod equal-using-type ((type duration-type) u v)
876 (equal u v))
878 ;; zzz das ist vielleicht ein bisschen zu woertlich implementiert
879 (defmethod lessp-using-type ((type duration-type) u v)
880 (let ((dt (make-instance 'date-time-type)))
881 (every (lambda (str)
882 (let ((s (parse dt str nil)))
883 (lessp-using-type dt
884 (datetime+duration s u)
885 (datetime+duration s v))))
886 '("1696-09-01T00:00:00Z"
887 "1697-02-01T00:00:00Z"
888 "1903-03-01T00:00:00Z"
889 "1903-07-01T00:00:00Z"))))
891 (defun datetime+duration (s d)
892 (destructuring-bind (syear smonth sday shour sminute ssecond szone) s
893 (destructuring-bind (dyear dmonth dday dhour dminute dsecond) d
894 (setf dhour (or dhour 0))
895 (setf dminute (or dminute 0))
896 (setf dsecond (or dsecond 0))
897 (labels ((floor3 (a low high)
898 (multiple-value-bind (u v)
899 (floor (- a low) (- high low))
900 (values u (+ low v))))
901 (maximum-day-in-month-for (yearvalue monthvalue)
902 (multiple-value-bind (m y)
903 (floor3 monthvalue 1 13)
904 (day-limit m (+ yearvalue y)))))
905 (multiple-value-bind (carry emonth) (floor3 (+ smonth dmonth) 1 13)
906 (let ((eyear (+ syear dyear carry))
907 (ezone szone))
908 (multiple-value-bind (carry esecond) (floor (+ ssecond dsecond) 60)
909 (multiple-value-bind (carry eminute)
910 (floor (+ sminute dminute carry) 60)
911 (multiple-value-bind (carry ehour)
912 (floor (+ shour dhour carry) 24)
913 (let* ((mdimf (maximum-day-in-month-for eyear emonth))
914 (tmpdays (max 1 (min sday mdimf)))
915 (eday (+ tmpdays dday carry)))
916 (loop
917 (let* ((mdimf (maximum-day-in-month-for eyear emonth))
918 (carry
919 (cond
920 ((< eday 1)
921 (setf eday (+ eday mdimf))
923 ((> eday mdimf)
924 (setf eday (- eday mdimf))
927 (return))))
928 (tmp (+ emonth carry)))
929 (multiple-value-bind (y m)
930 (floor3 tmp 1 13)
931 (setf emonth m)
932 (incf eyear y))))
933 (list eyear emonth eday ehour eminute esecond
934 ezone)))))))))))
936 (defun scan-to-strings (&rest args)
937 (coerce (nth-value 1 (apply #'cl-ppcre:scan-to-strings args)) 'list))
939 (defmethod parse/xsd ((type duration-type) e context)
940 (declare (ignore context))
941 (destructuring-bind (&optional minusp y m d tp h min s)
942 (scan-to-strings "(?x)
943 ^(-)? # minus
944 P(?:(\\d+)Y)? # years
945 (?:(\\d+)M)? # months
946 (?:(\\d+)D)? # days
947 (T # (time)
948 (?:(\\d+)H)? # hours
949 (?:(\\d+)M)? # minutes
950 (?:(\\d+(?:[.]\\d+)?)S)? # seconds
951 )?$"
953 (if (and (or y m d h min s)
954 (or (null tp) (or h min s)))
955 (let ((f (if minusp -1 1)))
956 (flet ((int (str)
957 (and str (* f (parse-integer str)))))
958 (list (int y) (int m) (int d) (int h) (int min)
959 (and s (* f (parse-number:parse-number s))))))
960 :error)))
963 ;;; dateTime
965 (defclass time-ordering-mixin (ordering-mixin) ())
967 (defxsd (date-time-type "dateTime") (xsd-type time-ordering-mixin)
969 (:documentation
970 "@short{The dateTime data type, representing a moment in time.}
972 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
973 the @a[http://www.w3.org/TR/xmlschema-2/#dateTime]{specification}.
975 @b{Implementation.} This type returns lists of the form
976 @code{(year month day hour minute second timezone)}. Each
977 value is an integer, except except for @code{second}, which is a real,
978 and @code{timezone} which is a real or @code{nil}.
979 A @code{timezone} of @code{nil} indicates UTC.
981 @b{Example.} @code{2002-10-10T12:00:00-05:00}
982 maps to @code{(2002 10 10 12 0 0 -5)}
984 @b{Parameters.} This type is ordered and allows the parameters
985 @slot{max-inclusive}, @slot{min-inclusive},
986 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
987 except within a timezone, see the spec for details."))
989 (defmethod equal-using-type ((type time-ordering-mixin) u v)
990 (equal u v))
992 ;; add zone-offset as a duration (if any), but keep a boolean in the
993 ;; zone-offset field indicating whether there was a time-zone
994 (defun normalize-date-time (u)
995 (destructuring-bind (year month day hour minute second zone-offset) u
996 (let ((v (list year month day hour minute second (and zone-offset t))))
997 (if zone-offset
998 (multiple-value-bind (h m)
999 (truncate zone-offset)
1000 (datetime+timezone v h (* m 100)))
1001 v))))
1003 (defun datetime+timezone (d h m)
1004 (datetime+duration d (list 0 0 0 h m 0)))
1006 (defmethod lessp-using-type ((type time-ordering-mixin) p q)
1007 (destructuring-bind (pyear pmonth pday phour pminute psecond pzone)
1008 (normalize-date-time p)
1009 (destructuring-bind (qyear qmonth qday qhour qminute qsecond qzone)
1010 (normalize-date-time q)
1011 (cond
1012 ((and pzone (not qzone))
1013 (lessp-using-type type p (datetime+timezone q 14 0)))
1014 ((and (not pzone) qzone)
1015 (lessp-using-type type (datetime+timezone p -14 0) q))
1017 ;; zzz hier sollen wir <> liefern bei Feldern, die in genau einer
1018 ;; der Zeiten fehlen. Wir stellen aber fehlende Felder derzeit
1019 ;; defaulted dar, koennen diese Situation also nicht feststellen.
1020 ;; Einen Unterschied sollte das nur machen, wenn Werte verschiedener
1021 ;; Datentypen miteinander verglichen werden. Das bieten wir einfach
1022 ;; nicht an.
1023 (loop
1024 for a in (list pyear pmonth pday phour pminute psecond)
1025 for b in (list qyear qmonth qday qhour qminute qsecond)
1027 (when (< a b)
1028 (return t))
1029 (when (> a b)
1030 (return nil))))))))
1032 (defun day-limit (m y)
1033 (cond
1034 ((and (eql m 2)
1035 (or (zerop (mod y 400))
1036 (and (zerop (mod y 4))
1037 (not (zerop (mod y 100))))))
1039 ((eql m 2) 28)
1040 ((if (<= m 7) (oddp m) (evenp m)) 31)
1041 (t 30)))
1043 (defun parse-time (minusp y m d h min s tz tz-sign tz-h tz-m
1044 &key (start 0) end)
1045 (declare (ignore tz start end)) ;zzz
1046 ;; parse into numbers
1047 (flet ((int (str)
1048 (and str (parse-integer str)))
1049 (num (str)
1050 (and str (parse-number:parse-number str))))
1051 (setf (values y m d h min s tz-h tz-m)
1052 (values (* (int y) (if minusp -1 1))
1053 (int m) (int d) (int h) (int min)
1054 (num s)
1055 (int tz-h) (int tz-m))))
1056 (let ((day-limit (day-limit m y)))
1057 ;; check ranges
1058 (cond
1059 ((and y m d h min s
1060 (plusp y)
1061 (<= 1 m 12)
1062 (<= 1 d day-limit)
1063 (<= 0 h 24)
1064 (<= 0 min 59)
1065 ;; zzz sind leap seconds immer erlaubt?
1066 (<= 0 s 60))
1067 ;; 24:00:00 must be canonicalized
1068 (when (and (eql h 24) (zerop min) (zerop s))
1069 (incf h)
1070 (incf d)
1071 (when (> d day-limit)
1072 (setf d 1)
1073 (incf m)
1074 (when (> m 12)
1075 (incf y))))
1076 (let ((tz-offset
1077 (when tz-h
1078 (* (if (equal tz-sign "-") -1 1)
1079 (+ tz-h (/ tz-m 100))))))
1080 (list (* y (if minusp -1 1)) m d h min s tz-offset)
1081 ;; (subseq ... start end)
1084 :error))))
1086 (defmethod parse/xsd ((type date-time-type) e context)
1087 (declare (ignore context))
1088 (destructuring-bind (&optional minusp y m d h min s tz tz-sign tz-h tz-m)
1089 (scan-to-strings "(?x)
1090 ^(-)? # opt. minus
1091 ((?:[1-9]\\d*)?\\d{4}) # year
1092 -(\\d\\d) # month
1093 -(\\d\\d) # day
1094 T # (time)
1095 (\\d\\d) # hour
1096 :(\\d\\d) # minute
1097 :(\\d+(?:[.]\\d+)?) # second
1098 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1101 (parse-time minusp y m d h min s tz tz-sign tz-h tz-m)))
1104 ;;; time
1106 (defxsd (time-type "time") (xsd-type time-ordering-mixin)
1108 (:documentation
1109 "@short{The time data type, representing a time of day.}
1111 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1112 the @a[http://www.w3.org/TR/xmlschema-2/#dateTime]{specification}.
1114 @b{Implementation.} This type returns the same kind of lists as
1115 @class{date-time-type}, except that the fields @code{year},
1116 @code{month} and @code{day} are filled with dummy values from the
1117 Gregorian year AD 1.
1119 @b{Parameters.} This type is ordered and allows the parameters
1120 @slot{max-inclusive}, @slot{min-inclusive},
1121 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1122 except within a timezone, see the spec for details."))
1124 (defmethod parse/xsd ((type time-type) e context)
1125 (declare (ignore context))
1126 (destructuring-bind (&optional h min s tz tz-sign tz-h tz-m)
1127 (scan-to-strings "(?x)
1128 ^(\\d\\d) # hour
1129 :(\\d\\d) # minute
1130 :(\\d+(?:[.]\\d+)?) # second
1131 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1134 (parse-time nil "1" "1" "1" h min s tz tz-sign tz-h tz-m
1135 :start 3)))
1138 ;;; date
1140 (defxsd (date-type "date") (xsd-type time-ordering-mixin)
1142 (:documentation
1143 "@short{The date data type, representing a day of the year.}
1145 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1146 the @a[http://www.w3.org/TR/xmlschema-2/#date]{specification}.
1148 @b{Implementation.} This type returns the same kind of lists as
1149 @class{date-time-type}, except that the fields @code{hour},
1150 @code{minute} and @code{second} are filled with dummy values from the
1151 Gregorian year AD 1.
1153 @b{Parameters.} This type is ordered and allows the parameters
1154 @slot{max-inclusive}, @slot{min-inclusive},
1155 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1156 except within a timezone, see the spec for details."))
1158 (defmethod parse/xsd ((type date-type) e context)
1159 (declare (ignore context))
1160 (destructuring-bind (&optional minusp y m d tz tz-sign tz-h tz-m)
1161 (scan-to-strings "(?x)
1162 ^(-)? # opt. minus
1163 ((?:[1-9]\\d*)?\\d{4}) # year
1164 -(\\d\\d) # month
1165 -(\\d\\d) # day
1166 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1169 (parse-time minusp y m d "0" "0" "0" tz tz-sign tz-h tz-m
1170 :end 3)))
1173 ;;; gYearMonth
1175 (defxsd (year-month-type "gYearMonth") (xsd-type time-ordering-mixin)
1177 (:documentation
1178 "@short{The gYearMonth data type, representing the calendar month of a
1179 specific year.}
1181 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1182 the @a[http://www.w3.org/TR/xmlschema-2/#gYearMonth]{specification}.
1184 @b{Implementation.} This type returns the same kind of lists as
1185 @class{date-time-type}, except that the fields @code{day}, @code{hour},
1186 @code{minute} and @code{second} are filled with dummy values from the
1187 Gregorian year AD 1.
1189 @b{Parameters.} This type is ordered and allows the parameters
1190 @slot{max-inclusive}, @slot{min-inclusive},
1191 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1192 except within a timezone, see the spec for details."))
1194 (defmethod parse/xsd ((type year-month-type) e context)
1195 (declare (ignore context))
1196 (destructuring-bind (&optional minusp y m)
1197 (scan-to-strings "(?x)
1198 ^(-)? # opt. minus
1199 ((?:[1-9]\\d*)?\\d{4}) # year
1200 -(\\d\\d) # month
1203 (parse-time minusp y m "1" "0" "0" "0" nil nil nil nil
1204 :end 2)))
1207 ;;; gYear
1209 (defxsd (year-type "gYear") (xsd-type time-ordering-mixin)
1211 (:documentation
1212 "@short{The gYear data type, representing a calendar year.}
1214 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1215 the @a[http://www.w3.org/TR/xmlschema-2/#gYear]{specification}.
1217 @b{Implementation.} This type returns the same kind of lists as
1218 @class{date-time-type}, except that the fields @code{month}, @code{day},
1219 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1220 from the Gregorian year AD 1.
1222 @b{Parameters.} This type is ordered and allows the parameters
1223 @slot{max-inclusive}, @slot{min-inclusive},
1224 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1225 except within a timezone, see the spec for details."))
1227 (defmethod parse/xsd ((type year-type) e context)
1228 (declare (ignore context))
1229 (destructuring-bind (&optional minusp y tz tz-sign tz-h tz-m)
1230 (scan-to-strings "(?x)
1231 ^(-)? # opt. minus
1232 ((?:[1-9]\\d*)?\\d{4}) # year
1233 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1236 (parse-time minusp y "1" "1" "0" "0" "0" tz tz-sign tz-h tz-m
1237 :end 1)))
1240 ;;; gMonthDay
1242 (defxsd (month-day-type "gMonthDay") (xsd-type time-ordering-mixin)
1244 (:documentation
1245 "@short{The gMonthDay data type, representing a calendar month and day.}
1247 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1248 the @a[http://www.w3.org/TR/xmlschema-2/#monthDay]{specification}.
1250 @b{Implementation.} This type returns the same kind of lists as
1251 @class{date-time-type}, except that the fields @code{year},
1252 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1253 from the Gregorian year AD 1.
1255 @b{Parameters.} This type is ordered and allows the parameters
1256 @slot{max-inclusive}, @slot{min-inclusive},
1257 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1258 except within a timezone, see the spec for details."))
1260 (defmethod parse/xsd ((type month-day-type) e context)
1261 (declare (ignore context))
1262 (destructuring-bind (&optional m d tz tz-sign tz-h tz-m)
1263 (scan-to-strings "(?x)
1264 ^--(\\d\\d) # month
1265 -(\\d\\d) # day
1266 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1269 (parse-time nil "1" m d "0" "0" "0" tz tz-sign tz-h tz-m
1270 :start 1 :end 3)))
1273 ;;; gDay
1275 (defxsd (day-type "gDay") (xsd-type time-ordering-mixin)
1277 (:documentation
1278 "@short{The gDay data type, representing a calendar day.}
1280 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1281 the @a[http://www.w3.org/TR/xmlschema-2/#gDay]{specification}.
1283 @b{Implementation.} This type returns the same kind of lists as
1284 @class{date-time-type}, except that the fields @code{year}, @code{month},
1285 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1286 from the Gregorian year AD 1.
1288 @b{Parameters.} This type is ordered and allows the parameters
1289 @slot{max-inclusive}, @slot{min-inclusive},
1290 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1291 except within a timezone, see the spec for details."))
1293 (defmethod parse/xsd ((type day-type) e context)
1294 (declare (ignore context))
1295 (destructuring-bind (&optional d tz tz-sign tz-h tz-m)
1296 (scan-to-strings "(?x)
1297 ---(\\d\\d) # day
1298 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1301 (parse-time nil "1" "1" d "0" "0" "0" tz tz-sign tz-h tz-m
1302 :start 3 :end 4)))
1305 ;;; gMonth
1307 (defxsd (month-type "gMonth") (xsd-type time-ordering-mixin)
1309 (:documentation
1310 "@short{The gMonth data type, representing a calendar month.}
1312 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1313 the @a[http://www.w3.org/TR/xmlschema-2/#gMonth]{specification}.
1315 @b{Implementation.} This type returns the same kind of lists as
1316 @class{date-time-type}, except that the fields @code{year}, @code{day},
1317 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1318 from the Gregorian year AD 1.
1320 @b{Parameters.} This type is ordered and allows the parameters
1321 @slot{max-inclusive}, @slot{min-inclusive},
1322 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1323 except within a timezone, see the spec for details."))
1325 (defmethod parse/xsd ((type month-type) e context)
1326 (declare (ignore context))
1327 (destructuring-bind (&optional m tz tz-sign tz-h tz-m)
1328 (scan-to-strings "(?x)
1329 ^--(\\d\\d) # month
1330 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1333 (parse-time nil "1" m "1" "0" "0" "0" tz tz-sign tz-h tz-m
1334 :start 2 :end 3)))
1337 ;;; boolean
1339 (defxsd (boolean-type "boolean") (xsd-type)
1341 (:documentation
1342 "@short{The boolean data type.}
1344 @b{Syntax.} \"1\", \"0\", \"true\", or \"false\".
1345 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#boolean]{specification}.
1347 @b{Implementation.} This type returns @code{t} or @code{nil}.
1349 @b{Parameters.} No parameters except for @fun{pattern} are available for
1350 this type."))
1352 (defmethod parse/xsd ((type boolean-type) e context)
1353 (declare (ignore context))
1354 (case (find-symbol e :keyword)
1355 ((:|true| :|1|) t)
1356 ((:|false| :|0|) nil)))
1359 ;;; base64Binary
1361 (defxsd (base64-binary-type "base64Binary") (xsd-type length-mixin)
1363 (:documentation
1364 "@short{The base64Binary data type.}
1366 @b{Syntax.} Normal Base64 syntax.
1367 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#base64Binary]{specification}.
1369 @b{Implementation.} This type returns an @code{(unsigned-byte 8)}
1370 vector.
1372 @b{Parameters.} This type allows restrictions on the length of the octet
1373 vector through the parameters @slot{exact-length}, @slot{min-length}, and
1374 @slot{max-length}."))
1376 (defmethod equal-using-type ((type base64-binary-type) u v)
1377 (equalp u v))
1379 (defmethod parse/xsd ((type base64-binary-type) e context)
1380 (declare (ignore context))
1381 (if (cl-ppcre:all-matches
1382 "(?x)
1383 ^(([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/]
1384 [ ]?[A-Za-z0-9+/][ ]?)*
1385 (([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/])
1386 | ([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[AEIMQUYcgkosw048][ ]?=)
1387 | ([A-Za-z0-9+/][ ]?[AQgw][ ]?=[ ]?=)))?$"
1389 (handler-case
1390 (cl-base64:base64-string-to-usb8-array e)
1391 (warning (c)
1392 (error "unexpected failure in Base64 decoding: ~A" c)))
1393 :error))
1396 ;;; hexBinary
1398 (defxsd (hex-binary-type "hexBinary") (xsd-type length-mixin)
1400 (:documentation
1401 "@short{The hexBinary data type.}
1403 @b{Syntax.} A sequence of two-digit hexadecimal numbers representing
1404 one octet each.
1405 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#hexBinary]{specification}.
1407 @b{Implementation.} This type returns an @code{(unsigned-byte 8)}
1408 vector.
1410 @b{Parameters.} This type allows restrictions on the length of the octet
1411 vector through the parameters @slot{exact-length}, @slot{min-length}, and
1412 @slot{max-length}."))
1414 (defmethod equal-using-type ((type hex-binary-type) u v)
1415 (equalp u v))
1417 (defmethod parse/xsd ((type hex-binary-type) e context)
1418 (declare (ignore context))
1419 (if (evenp (length e))
1420 (let ((result
1421 (make-array (/ (length e) 2) :element-type '(unsigned-byte 8))))
1422 (loop
1423 for i from 0 below (length e) by 2
1424 for j from 0
1426 (setf (elt result j)
1427 (handler-case
1428 (parse-integer e :start i :end (+ i 2) :radix 16)
1429 (error ()
1430 (return :error))))
1431 finally (return result)))
1432 :error))
1435 ;;; float
1437 (defxsd (float-type "float") (xsd-type ordering-mixin)
1439 (:documentation
1440 "@short{The float data type.}
1442 @b{Syntax.} A floating-point number in a \"scientific notation\".
1443 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#float]{specification}.
1445 @b{Implementation.} This type returns a @code{single-float} or, on
1446 implementations where Infinity and Nan cannot be represented as such,
1447 a special symbol that is treated as if it was Infinity or NaN by the
1448 built-in ordering.
1450 @b{Parameters.} This type is ordered and allows the parameters
1451 @slot{max-inclusive}, @slot{min-inclusive},
1452 @slot{max-exclusive}, and @slot{min-exclusive}."))
1454 (defmethod equal-using-type ((type float-type) u v)
1455 #+(or sbcl allegro) (= u v)
1456 #-(or sbcl allegro) (float= u v))
1458 (defmethod lessp-using-type ((type float-type) u v)
1459 #+(or sbcl allegro) (< u v)
1460 #-(or sbcl allegro) (float< u v))
1462 ;; this one is more complex than would seem necessary, because too-large
1463 ;; and too-small values must be rounded to infinity rather than erroring out
1464 (defun parse-float (e min max +inf -inf nan)
1465 (cond
1466 ((equal e "INF") +inf)
1467 ((equal e "-INF") -inf)
1468 ((equal e "Nan") nan)
1470 (destructuring-bind (&optional a b)
1471 (scan-to-strings "^([^eE]+)(?:[eE]([^eE]+))?$" e)
1472 (if a
1473 (let* ((mantissa (parse/xsd (make-instance 'decimal-type) a nil))
1474 (exponent
1475 (when b
1476 (parse/xsd (make-instance 'integer-type) b nil))))
1477 (if (or (eq mantissa :error) (eq exponent :error))
1478 :error
1479 (let ((ratio (* mantissa (expt 10 (or exponent 1)))))
1480 (cond
1481 ((< ratio min) -inf)
1482 ((> ratio max) +inf)
1483 (t (float ratio min))))))
1484 :error)))))
1486 ;; zzz nehme hier an, dass single-float in IEEE single float ist.
1487 ;; Das stimmt unter LispWorks bestimmt wieder nicht.
1488 (defmethod parse/xsd ((type float-type) e context)
1489 (declare (ignore context))
1490 (parse-float e
1491 most-negative-single-float
1492 most-positive-single-float
1493 single-float-positive-infinity
1494 single-float-negative-infinity
1495 single-float-nan))
1498 ;;; decimal
1500 (defgeneric fraction-digits (data-type)
1501 (:documentation
1502 "@arg[data-type]{a subtype of @class{decimal-type}}
1503 @return{an integer, or @code{nil}}
1504 This slot reader returns the type's
1505 @a[http://www.w3.org/TR/xmlschema-2/#rf-fractionDigits]{fractionDigits facet},
1506 or @code{nil} if none was specified.
1507 @see{total-digits}"))
1509 (defgeneric total-digits (data-type)
1510 (:documentation
1511 "@arg[data-type]{a subtype of @class{decimal-type}}
1512 @return{an integer, or @code{nil}}
1513 This slot reader returns the type's
1514 @a[http://www.w3.org/TR/xmlschema-2/#rf-totalDigits]{totalDigits facet},
1515 or @code{nil} if none was specified.
1516 @see{fraction-digits}"))
1518 (defxsd (decimal-type "decimal") (xsd-type ordering-mixin)
1519 ((fraction-digits :initform nil
1520 :initarg :fraction-digits
1521 :accessor fraction-digits)
1522 (total-digits :initform nil
1523 :initarg :total-digits
1524 :accessor total-digits))
1525 (:documentation
1526 "@short{The decimal data type.}
1528 @b{Syntax.} A rational number, written using an optional decimal point
1529 and decimal places.
1530 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#decimal]{specification}.
1532 @b{Implementation.} This type returns a @code{rational}.
1534 @b{Parameters.} This type is ordered and allows the parameters
1535 @slot{max-inclusive}, @slot{min-inclusive},
1536 @slot{max-exclusive}, and @slot{min-exclusive}.
1538 In addition, the facets @slot{fraction-digits} @slot{total-digits}
1539 are recognized."))
1541 (defmethod describe-facets progn ((object decimal-type) stream)
1542 (dolist (slot '(fraction-digits total-digits))
1543 (let ((value (slot-value object slot)))
1544 (when value
1545 (format stream " ~A ~A"
1546 (intern (symbol-name slot) :keyword)
1547 value)))))
1549 (defmethod parse-parameter
1550 ((class-name (eql 'decimal-type))
1551 (type-name t)
1552 (param (eql :fraction-digits))
1553 value)
1554 (parse (make-instance 'non-negative-integer-type) value nil))
1556 (defmethod parse-parameter
1557 ((class-name (eql 'decimal-type))
1558 (type-name t)
1559 (param (eql :total-digits))
1560 value)
1561 (parse (make-instance 'positive-integer-type) value nil))
1563 (defmethod lessp-using-type ((type decimal-type) u v)
1564 (< u v))
1566 (defmethod equal-using-type ((type decimal-type) u v)
1567 (= u v))
1569 (defmethod validp/xsd and ((type decimal-type) v context)
1570 (declare (ignore context))
1571 (with-slots (fraction-digits total-digits) type
1572 (and (or (null fraction-digits)
1573 (let* ((betrag (abs v))
1574 (fraction (- betrag (truncate betrag)))
1575 (scaled (* fraction (expt 10 fraction-digits))))
1576 (zerop (mod scaled 1))))
1577 (or (null total-digits)
1578 (let ((scaled (abs v)))
1579 (loop
1580 until (zerop (mod scaled 1))
1581 do (setf scaled (* scaled 10)))
1582 (< scaled (expt 10 total-digits)))))))
1584 (defmethod parse/xsd ((type decimal-type) e context)
1585 (declare (ignore context))
1586 (destructuring-bind (&optional a b)
1587 (scan-to-strings "^([+-]?\\d*)(?:[.](\\d+))?$" e)
1588 (if (plusp (+ (length a) (length b)))
1589 (+ (if (plusp (length a))
1590 (parse-integer a)
1592 (if (plusp (length b))
1593 (/ (parse-integer b) (expt 10 (length b)))
1595 :error)))
1598 ;;; double
1600 (defxsd (double-type "double") (xsd-type ordering-mixin)
1602 (:documentation
1603 "@short{The double data type.}
1605 @b{Syntax.} A floating-point number in a \"scientific notation\".
1606 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#double]{specification}.
1608 @b{Implementation.} This type returns a @code{double-float} or, on
1609 implementations where Infinity and Nan cannot be represented as such,
1610 a special symbol that is treated as if it was Infinity or NaN by the
1611 built-in ordering.
1613 @b{Parameters.} This type is ordered and allows the parameters
1614 @slot{max-inclusive}, @slot{min-inclusive},
1615 @slot{max-exclusive}, and @slot{min-exclusive}."))
1617 (defmethod equal-using-type ((type double-type) u v)
1618 #+(or sbcl allegro) (= u v)
1619 #-(or sbcl allegro) (float= u v))
1621 (defmethod lessp-using-type ((type double-type) u v)
1622 #+(or sbcl allegro) (< u v)
1623 #-(or sbcl allegro) (float< u v))
1625 ;; zzz nehme hier an, dass double-float in IEEE double float ist.
1626 ;; Auch das ist nicht garantiert.
1627 (defmethod parse/xsd ((type double-type) e context)
1628 (declare (ignore context))
1629 (parse-float e
1630 most-negative-double-float
1631 most-positive-double-float
1632 double-float-positive-infinity
1633 double-float-negative-infinity
1634 double-float-nan))
1637 ;;; AnyURi
1639 (defxsd (any-uri-type "anyURI") (xsd-type length-mixin)
1641 (:documentation
1642 "@short{The anyURI data type.}
1644 @b{Syntax.} An arbitrary string (!).
1645 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#anyURI]{specification}.
1647 @b{Implementation.} This type returns a normalized string in which
1648 special characters have been escaped.
1650 @b{Parameters.} This type allows restrictions on the length of the
1651 normalized string through the parameters @slot{exact-length},
1652 @slot{min-length}, and @slot{max-length}."))
1654 (defmethod equal-using-type ((type any-uri-type) u v)
1655 (equal u v))
1657 (defmethod parse/xsd ((type any-uri-type) e context)
1658 (cxml-rng::escape-uri e))
1661 ;;; QName
1662 ;;; NOTATION
1664 (defclass qname-like (xsd-type length-mixin) ())
1666 (defxsd (qname-type "QName") (qname-like)
1668 (:documentation
1669 "@short{The QName data type.}
1671 @b{Syntax.} A Qualified Name, as per the \"Namespaces in XML\"
1672 specification. The namespace prefix must be bound to a namespace URI
1673 in the context.
1674 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#QName]{specification}.
1676 @b{Context dependent.} This type is context dependent and requires
1677 the @code{context} argument to @fun{parse} and @fun{validp}.
1679 @b{Implementation.} This type returns a structure with two components,
1680 the namespace URI and the local name. fixme: and the original length.
1681 fixme: export this structure.
1683 @b{Parameters.} This type allows restrictions on the length of the
1684 original QName through the parameters @slot{exact-length},
1685 @slot{min-length}, and @slot{max-length}."))
1687 (defxsd (notation-type "NOTATION") (qname-like)
1689 (:documentation
1690 "@short{The NOTATION data type.}
1692 @b{Syntax.} A qualified name.
1693 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NOTATION]{specification}.
1695 @b{Implementation.} This type is treated exactly like
1696 @class{qname-type}, as specified in
1697 @a[http://relaxng.org/xsd-20010907.html]{Guidelines for using W3C XML
1698 Schema Datatypes with RELAX NG}.
1700 @b{Parameters.} This type allows restrictions on the length of the
1701 original QName through the parameters @slot{exact-length},
1702 @slot{min-length}, and @slot{max-length}."))
1704 (defstruct (qname (:constructor make-qname (uri lname length)))
1706 lname
1707 length)
1709 (defmethod length-using-type ((type qname-like) e)
1710 (qname-length e))
1712 (defmethod equal-using-type ((type qname-like) u v)
1713 (and (equal (qname-uri u) (qname-uri v))
1714 (equal (qname-lname u) (qname-lname v))))
1716 (defun namep (str)
1717 (and (not (zerop (length str)))
1718 (cxml::name-start-rune-p (elt str 0))
1719 (every #'cxml::name-rune-p str)))
1721 (defmethod type-context-dependent-p ((type qname-like))
1724 (defmethod parse/xsd ((type qname-like) e context)
1725 (handler-case
1726 (if (namep e)
1727 (multiple-value-bind (prefix local-name) (cxml::split-qname e)
1728 (let ((uri (when prefix
1729 (context-find-namespace-binding context prefix))))
1730 (if (and prefix (not uri))
1731 :error
1732 (make-qname uri local-name (length e)))))
1733 :error)
1734 (cxml:well-formedness-violation ()
1735 :error)))
1738 ;;; string
1740 (defxsd (xsd-string-type "string") (xsd-type length-mixin)
1742 (:documentation
1743 "@short{The string data type.}
1745 @b{Syntax.} An arbitrary string.
1746 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#string]{specification}.
1748 @b{Implementation.} Returns the string unchanged. This is the only
1749 XSD type that does not normalize or replace whitespace.
1751 @b{Parameters.} This type allows restrictions on the length of the
1752 string through the parameters @slot{exact-length},
1753 @slot{min-length}, and @slot{max-length}."))
1755 (defmethod equal-using-type ((type xsd-string-type) u v)
1756 (equal u v))
1758 (defmethod munge-whitespace ((type xsd-string-type) e)
1761 (defmethod parse/xsd ((type xsd-string-type) e context)
1765 ;;;;
1766 ;;;; Derived types
1767 ;;;;
1769 ;;; normalizedString
1771 (defxsd (normalized-string-type "normalizedString") (xsd-string-type)
1773 (:documentation
1774 "@short{The normalizedString data type, derived from string.}
1776 @b{Syntax.} An arbitrary string.
1777 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#normalizedString]{specification}.
1779 @b{Implementation.} Returns the string with whitespace replaced.
1781 I.e., each whitespace character is replaced by a space
1782 (character code 32), but multiple spaces, as well as
1783 leading and trailing spaces will still be returned.
1785 (This is the only XSD type that replaces whitespace in this way.)
1787 @b{Parameters.} This type allows restrictions on the length of the
1788 normalized string through the parameters @slot{exact-length},
1789 @slot{min-length}, and @slot{max-length}."))
1791 (defmethod munge-whitespace ((type normalized-string-type) e)
1792 (replace-whitespace e))
1795 ;;; token
1797 (defxsd (xsd-token-type "token") (normalized-string-type)
1799 (:documentation
1800 "@short{The token data type, derived from normalizedString.}
1802 @b{Syntax.} An arbitrary string.
1803 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#token]{specification}.
1805 @b{Implementation.} Returns the string with normalized whitespace.
1807 I.e., each whitespace character is replaced by a space
1808 (character code 32), multiple spaces are collapsed into one character,
1809 and leading and trailing spaces will be removed.
1811 (This is the standard behaviour of all XSD types with the exception of
1812 token's supertypes @class{string-type} and @class{normalized-string-type}.)
1814 @b{Parameters.} This type allows restrictions on the length of the
1815 normalized string through the parameters @slot{exact-length},
1816 @slot{min-length}, and @slot{max-length}."))
1818 (defmethod munge-whitespace ((type xsd-token-type) e)
1819 (normalize-whitespace e))
1822 ;;; language
1824 (defmacro precompile (pattern)
1825 `(load-time-value (list (pattern-scanner ,pattern))))
1827 (defxsd (language-type "language") (xsd-token-type)
1828 ((patterns :initform (precompile "[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*")))
1829 (:documentation
1830 "@short{The language data type, derived from token.}
1832 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#language]{specification}.
1834 @b{Restrictions.} This type restricts its supertype @class{token-type}
1835 to strings of the pattern \"[a-zA-Z]{1,8@}(-[a-zA-Z0-9]{1,8@})*\".
1837 @b{Parameters and implementation.} Unchanged from the supertype."))
1840 ;;; Name
1842 (defxsd (name-type "Name") (xsd-token-type)
1843 ((patterns :initform (precompile "\\i\\c*")))
1844 (:documentation
1845 "@short{The Name data type, derived from token.}
1847 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#Name]{specification}.
1849 @b{Restrictions.} This type restricts its supertype @class{token-type}
1850 to strings of the pattern \"\\i\\c*\".
1852 @b{Parameters and implementation.} Unchanged from the supertype."))
1855 ;;; NCName
1857 (defxsd (ncname-type "NCName") (name-type)
1858 ((patterns :initform (precompile "[\\i-[:]][\\c-[:]]*")))
1859 (:documentation
1860 "@short{The NCName data type, derived from Name.}
1862 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NCName]{specification}.
1864 @b{Restrictions.} This type restricts its supertype @class{name-type}
1865 to strings of the pattern \"[\\i-[:]][\\c-[:]]*\".
1867 @b{Parameters and implementation.} Unchanged from the supertype."))
1869 (defmethod equal-using-type ((type ncname-type) u v)
1870 (equal u v))
1872 (defmethod parse/xsd ((type ncname-type) e context)
1876 ;;; ID
1878 (defxsd (xsd-id-type "ID") (ncname-type)
1880 (:documentation
1881 "@short{The ID data type, derived from NCName.}
1883 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#ID]{specification}.
1885 @b{Restrictions.} None.
1887 @b{ID type.} This type has the ID-type 'ID'for the purposes of DTD
1888 compatibility. See @a[http://relaxng.org/xsd-20010907.html]{Guidelines
1889 for using W3C XML Schema Datatypes with RELAX NG}.
1891 @b{Parameters and implementation.} Unchanged from the supertype.
1893 @see{id-type}"))
1895 (defmethod type-id-type ((type xsd-id-type))
1896 :id)
1899 ;;; IDREF
1901 (defxsd (xsd-idref-type "IDREF") (xsd-id-type)
1903 (:documentation
1904 "@short{The IDREF data type, derived from ID.}
1906 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#IDREF]{specification}.
1908 @b{Restrictions.} None.
1910 @b{ID type.} This type has the ID-type 'IDREF'for the purposes of DTD
1911 compatibility. See @a[http://relaxng.org/xsd-20010907.html]{Guidelines
1912 for using W3C XML Schema Datatypes with RELAX NG}.
1914 @b{Parameters and implementation.} Unchanged from the supertype.
1916 @see{idref-type}"))
1918 (defmethod type-id-type ((type xsd-idref-type))
1919 :idref)
1922 ;;; IDREFS
1924 (defxsd (xsd-idrefs-type "IDREFS") (enumeration-type)
1925 ((word-type :initform (make-instance 'xsd-idref-type)))
1926 (:documentation
1927 "@short{The IDREFS data type, an enumeration.}
1929 @b{Syntax.} A whitespace-separated sequence of @class{xsd-idref-type}
1930 values, with at least one element.
1932 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#IDREFS]{specification}.
1934 @b{Implementation.} This type returns a list of the values as returned by
1935 @class{xsd-idref-type}.
1937 @b{ID type.} This type has the ID-type 'IDREFS'for the purposes of DTD
1938 compatibility. See @a[http://relaxng.org/xsd-20010907.html]{Guidelines
1939 for using W3C XML Schema Datatypes with RELAX NG}.
1941 @b{Parameters.} This type allows restrictions on the number of values
1942 through the parameters @slot{exact-length}, @slot{min-length}, and
1943 @slot{max-length}.
1945 @see{idrefs-type}"))
1947 (defmethod type-id-type ((type xsd-idrefs-type))
1948 :idrefs)
1951 ;;; ENTITY
1953 (defxsd (entity-type "ENTITY") (ncname-type)
1955 (:documentation
1956 "@short{The ENTITY data type, derived from NCName.}
1958 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#ENTITY]{specification}.
1960 @b{Restrictions.} This type restricts its supertype @class{ncname-type}
1961 to names that have been declared as unparsed entities in the context.
1963 @b{Context dependent.} This type is context dependent and requires
1964 the @code{context} argument to @fun{parse} and @fun{validp}.
1966 @b{Parameters and implementation.} Unchanged from the supertype."))
1968 (defmethod type-context-dependent-p ((type entity-type))
1971 (defmethod parse/xsd ((type entity-type) e context)
1972 (if (context-find-unparsed-entity context e)
1974 :error))
1977 ;;; ENTITIES
1979 (defxsd (entities-type "ENTITIES") (enumeration-type)
1980 ((word-type :initform (make-instance 'entity-type)))
1981 (:documentation
1982 "@short{The ENTITIES data type, an enumeration.}
1984 @b{Syntax.} A whitespace-separated sequence of @class{entity-type}
1985 values, with at least one element.
1987 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#ENTITIES]{specification}.
1989 @b{Implementation.} This type returns a list of the values as returned by
1990 @class{entity-type}.
1992 @b{Context dependent.} This type is context dependent and requires
1993 the @code{context} argument to @fun{parse} and @fun{validp}.
1995 @b{Parameters.} This type allows restrictions on the number of values
1996 through the parameters @slot{exact-length}, @slot{min-length}, and
1997 @slot{max-length}."))
2000 ;;; NMTOKEN
2002 (defxsd (nmtoken-type "NMTOKEN") (xsd-token-type)
2003 ((patterns :initform (precompile "\\c+")))
2004 (:documentation
2005 "@short{The NMTOKEN data type, derived from token.}
2007 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NMTOKEN]{specification}.
2009 @b{Restrictions.} This type restricts its supertype @class{token-type}
2010 to strings of the pattern \"\\c+\".
2012 @b{Parameters and implementation.} Unchanged from the supertype."))
2015 ;;; NMTOKENS
2017 (defxsd (nmtokens-type "NMTOKENS") (enumeration-type)
2018 ((word-type :initform (make-instance 'nmtoken-type)))
2019 (:documentation
2020 "@short{The NMTOKENS data type, an enumeration.}
2022 @b{Syntax.} A whitespace-separated sequence of @class{nmtoken-type}
2023 values, with at least one element.
2025 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NMTOKENS]{specification}.
2027 @b{Implementation.} This type returns a list of the values as returned by
2028 @class{nmtoken-type}.
2030 @b{Parameters.} This type allows restrictions on the number of values
2031 through the parameters @slot{exact-length}, @slot{min-length}, and
2032 @slot{max-length}."))
2035 ;;; integer
2037 (defxsd (integer-type "integer") (decimal-type)
2039 (:documentation
2040 "@short{The integer data type, derived from decimal.}
2042 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#integer]{specification}.
2044 @b{Syntax.} An integer, written it the decimal system without leading
2045 zeros. No decimal point is permitted.
2047 @b{Implementation.} This type returns an @code{integer}.
2049 @b{Parameters and implementation.} Unchanged from the supertype."))
2051 ;; period is forbidden, so there's no point in letting decimal handle parsing
2052 ;; fixme: sind fuehrende nullen nun erlaubt oder nicht? die spec sagt ja,
2053 ;; das pattern im schema nicht.
2054 (defmethod parse/xsd ((type integer-type) e context)
2055 (declare (ignore context))
2056 (if (cl-ppcre:all-matches "^[+-]?(?:[1-9]\\d*|0)$" e)
2057 (parse-number:parse-number e)
2058 :error))
2061 ;;; nonPositiveInteger
2063 (defxsd (non-positive-integer-type "nonPositiveInteger") (integer-type)
2065 (:documentation
2066 "@short{The nonPositiveInteger data type, derived from integer.}
2068 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#nonPositiveInteger]{specification}.
2070 @b{Restrictions.} This type allows only values <= 0.
2072 @b{Parameters and implementation.} Unchanged from the supertype."))
2074 (defun min* (a b)
2075 (cond
2076 ((null a) b)
2077 ((null b) a)
2078 (t (min a b))))
2080 (defun max* (a b)
2081 (cond
2082 ((null a) b)
2083 ((null b) a)
2084 (t (max a b))))
2086 (defmethod initialize-instance :after ((type non-positive-integer-type) &key)
2087 (setf (max-inclusive type)
2088 (min* 0 (max-inclusive type))))
2091 ;;; nonPositiveInteger
2093 (defxsd (negative-integer-type "negativeInteger") (non-positive-integer-type)
2095 (:documentation
2096 "@short{The negativeInteger data type, derived from nonPositiveInteger.}
2098 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#negativeInteger]{specification}.
2100 @b{Restrictions.} This type allows only values < 0.
2102 @b{Parameters and implementation.} Unchanged from the supertype."))
2104 (defmethod initialize-instance :after ((type negative-integer-type) &key)
2105 (setf (max-inclusive type)
2106 (min* -1 (max-inclusive type))))
2109 ;;; long
2111 (defxsd (long-type "long") (integer-type)
2113 (:documentation
2114 "@short{The long data type, derived from integer.}
2116 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#long]{specification}.
2118 @b{Restrictions.} This type allows only values from the interval
2119 [-2^63, 2^63-1].
2121 @b{Parameters and implementation.} Unchanged from the supertype."))
2123 (defmethod initialize-instance :after ((type long-type) &key)
2124 (setf (max-inclusive type) (min* 9223372036854775807 (max-inclusive type)))
2125 (setf (min-inclusive type) (max* -9223372036854775808 (min-inclusive type))))
2128 ;;; int
2130 (defxsd (int-type "int") (long-type)
2132 (:documentation
2133 "@short{The int data type, derived from long.}
2135 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#int]{specification}.
2137 @b{Restrictions.} This type allows only values from the interval
2138 [-2^31, 2^31-1].
2140 @b{Parameters and implementation.} Unchanged from the supertype."))
2142 (defmethod initialize-instance :after ((type int-type) &key)
2143 (setf (max-inclusive type) (min* 2147483647 (max-inclusive type)))
2144 (setf (min-inclusive type) (max* -2147483648 (min-inclusive type))))
2147 ;;; short
2149 (defxsd (short-type "short") (int-type)
2151 (:documentation
2152 "@short{The short data type, derived from int.}
2154 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#short]{specification}.
2156 @b{Restrictions.} This type allows only values from the interval
2157 [-2^15, 2^15-1].
2159 @b{Parameters and implementation.} Unchanged from the supertype."))
2161 (defmethod initialize-instance :after ((type short-type) &key)
2162 (setf (max-inclusive type) (min* 32767 (max-inclusive type)))
2163 (setf (min-inclusive type) (max* -32768 (min-inclusive type))))
2166 ;;; byte
2168 (defxsd (byte-type "byte") (short-type)
2170 (:documentation
2171 "@short{The byte data type, derived from short.}
2173 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#byte]{specification}.
2175 @b{Restrictions.} This type allows only values from the interval
2176 [-128, 127].
2178 @b{Parameters and implementation.} Unchanged from the supertype."))
2180 (defmethod initialize-instance :after ((type byte-type) &key)
2181 (setf (max-inclusive type) (min* 127 (max-inclusive type)))
2182 (setf (min-inclusive type) (max* -128 (min-inclusive type))))
2185 ;;; nonNegativeInteger
2187 (defxsd (non-negative-integer-type "nonNegativeInteger") (integer-type)
2189 (:documentation
2190 "@short{The nonNegativeInteger data type, derived from integer.}
2192 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#nonNegativeInteger]{specification}.
2194 @b{Restrictions.} This type allows only values >= 0.
2196 @b{Parameters and implementation.} Unchanged from the supertype."))
2198 (defmethod initialize-instance :after ((type non-negative-integer-type) &key)
2199 (setf (min-inclusive type) (max* 0 (min-inclusive type))))
2202 ;;; unsignedLong
2204 (defxsd (unsigned-long-type "unsignedLong") (non-negative-integer-type)
2206 (:documentation
2207 "@short{The unsignedLong data type, derived from nonNegativeInteger.}
2209 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedLong]{specification}.
2211 @b{Restrictions.} This type allows only values from the interval
2212 [0, 2^64-1].
2214 @b{Parameters and implementation.} Unchanged from the supertype."))
2216 (defmethod initialize-instance :after ((type unsigned-long-type) &key)
2217 (setf (max-inclusive type) (min* 18446744073709551615 (max-inclusive type))))
2220 ;;; unsignedInt
2222 (defxsd (unsigned-int-type "unsignedInt") (unsigned-long-type)
2224 (:documentation
2225 "@short{The unsignedInt data type, derived from unsignedLong.}
2227 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedInt]{specification}.
2229 @b{Restrictions.} This type allows only values from the interval
2230 [0, 2^32-1].
2232 @b{Parameters and implementation.} Unchanged from the supertype."))
2234 (defmethod initialize-instance :after ((type unsigned-int-type) &key)
2235 (setf (max-inclusive type) (min* 4294967295 (max-inclusive type))))
2238 ;;; unsignedShort
2240 (defxsd (unsigned-short-type "unsignedShort") (unsigned-int-type)
2242 (:documentation
2243 "@short{The unsignedShort data type, derived from unsignedInt.}
2245 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedShort]{specification}.
2247 @b{Restrictions.} This type allows only values from the interval
2248 [0, 2^16-1].
2250 @b{Parameters and implementation.} Unchanged from the supertype."))
2252 (defmethod initialize-instance :after ((type unsigned-short-type) &key)
2253 (setf (max-inclusive type) (min* 65535 (max-inclusive type))))
2256 ;;; unsignedByte
2258 (defxsd (unsigned-byte-type "unsignedByte") (unsigned-short-type)
2260 (:documentation
2261 "@short{The unsignedByte data type, derived from unsignedInt.}
2263 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedByte]{specification}.
2265 @b{Restrictions.} This type allows only values from the interval
2266 [0, 255].
2268 @b{Parameters and implementation.} Unchanged from the supertype."))
2270 (defmethod initialize-instance :after ((type unsigned-byte-type) &key)
2271 (setf (max-inclusive type) (min* 255 (max-inclusive type))))
2274 ;;; positiveInteger
2276 (defxsd (positive-integer-type "positiveInteger") (non-negative-integer-type)
2278 (:documentation
2279 "@short{The positiveInteger data type, derived from nonNegativeInteger.}
2281 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#positiveInteger]{specification}.
2283 @b{Restrictions.} This type allows only values > 0.
2285 @b{Parameters and implementation.} Unchanged from the supertype."))
2287 (defmethod initialize-instance :after ((type positive-integer-type) &key)
2288 (setf (min-inclusive type) (max* 1 (min-inclusive type))))
2291 ;;;; backpatch ID types
2293 (defvar *id-type* (make-instance 'id-type))
2294 (defvar *idref-type* (make-instance 'idref-type))
2295 (defvar *idrefs-type* (make-instance 'idrefs-type))