6 (define-condition rng-error
(simple-error) ())
8 (defun rng-error (fmt &rest args
)
9 (error 'rng-error
:format-control fmt
:format-arguments args
))
14 (defvar *datatype-library
*)
15 (defvar *entity-resolver
*)
16 (defvar *external-href-stack
*)
17 (defvar *include-href-stack
*)
19 (defun parse-relax-ng (input &key entity-resolver
)
21 (klacks:with-open-source
(source (cxml:make-source input
))
22 (klacks:find-event source
:start-element
)
23 (let ((*datatype-library
* "")
24 (*entity-resolver
* entity-resolver
)
25 (*external-href-stack
* '())
26 (*include-href-stack
* '()))
28 (cxml:xml-parse-error
(c)
29 (rng-error "Cannot parse schema: ~A" c
))))
32 ;;;; pattern structures
37 (defstruct (%combination
(:include pattern
) (:conc-name
"PATTERN-"))
40 (defstruct (%named-pattern
(:include pattern
) (:conc-name
"PATTERN-"))
43 (defstruct (element (:include %named-pattern
) (:conc-name
"PATTERN-"))
46 (defstruct (attribute (:include %named-pattern
) (:conc-name
"PATTERN-"))
49 (defstruct (group (:include %combination
) (:conc-name
"PATTERN-")))
50 (defstruct (interleave (:include %combination
) (:conc-name
"PATTERN-")))
51 (defstruct (choice (:include %combination
) (:conc-name
"PATTERN-")))
52 (defstruct (optional (:include %combination
) (:conc-name
"PATTERN-")))
53 (defstruct (zero-or-more (:include %combination
) (:conc-name
"PATTERN-")))
54 (defstruct (one-or-more (:include %combination
) (:conc-name
"PATTERN-")))
55 (defstruct (list-pattern (:include %combination
) (:conc-name
"PATTERN-")))
56 (defstruct (mixed (:include %combination
) (:conc-name
"PATTERN-")))
58 (defstruct (ref (:include %named-pattern
) (:conc-name
"PATTERN-")))
60 (defstruct (parent-ref (:include %named-pattern
) (:conc-name
"PATTERN-")))
62 (defstruct (empty (:include pattern
) (:conc-name
"PATTERN-")))
63 (defstruct (text (:include pattern
) (:conc-name
"PATTERN-")))
65 (defstruct (%typed-pattern
(:include pattern
) (:conc-name
"PATTERN-"))
68 (defstruct (value (:include %typed-pattern
) (:conc-name
"PATTERN-"))
71 (defstruct (data (:include %typed-pattern
) (:conc-name
"PATTERN-"))
76 (defstruct (not-allowed (:include pattern
) (:conc-name
"PATTERN-")))
78 (defstruct (grammar (:include pattern
) (:conc-name
"PATTERN-"))
107 (defvar *rng-namespace
* "http://relaxng.org/ns/structure/1.0")
109 (defun skip-foreign (source)
110 (when (equal (klacks:current-uri source
) *rng-namespace
*)
111 (rng-error "invalid schema: ~A not allowed here"
112 (klacks:current-lname source
)))
113 (klacks:serialize-element source nil
))
115 (defun attribute (lname attrs
)
116 (let ((a (sax:find-attribute-ns
"" lname attrs
)))
118 (sax:attribute-value a
)
128 (defun ntc (lname attrs
)
129 ;; used for (n)ame, (t)ype, and (c)ombine, this also strings whitespace
130 (let ((a (sax:find-attribute-ns
"" lname attrs
)))
132 (string-trim *whitespace
* (sax:attribute-value a
))
135 (defmacro with-datatype-library
(attrs &body body
)
136 `(invoke-with-datatype-library (lambda () ,@body
) ,attrs
))
138 (defun invoke-with-datatype-library (fn attrs
)
139 (let* ((dl (attribute "datatypeLibrary" attrs
))
140 (*datatype-library
* (if dl
(escape-uri dl
) *datatype-library
*)))
143 (defun p/pattern
(source)
144 (let* ((lname (klacks:current-lname source
))
145 (attrs (klacks:list-attributes source
))
146 (ns (attribute "ns" attrs
)))
147 (with-datatype-library attrs
148 (case (find-symbol lname
:keyword
)
149 (:|element|
(p/element source
(ntc "name" attrs
) ns
))
150 (:|attribute|
(p/attribute source
(ntc "name" attrs
) ns
))
151 (:|group|
(p/combination
#'make-group source ns
))
152 (:|interleave|
(p/combination
#'make-interleave source ns
))
153 (:|choice|
(p/combination
#'make-choice source ns
))
154 (:|optional|
(p/combination
#'make-optional source ns
))
155 (:|zeroOrMore|
(p/combination
#'make-zero-or-more source ns
))
156 (:|oneOrMore|
(p/combination
#'make-one-or-more source ns
))
157 (:|list|
(p/combination
#'make-list-pattern source ns
))
158 (:|mixed|
(p/combination
#'make-mixed source ns
))
159 (:|ref|
(p/ref source ns
))
160 (:|parentRef|
(p/parent-ref source ns
))
161 (:|empty|
(p/empty source ns
))
162 (:|text|
(p/text source ns
))
163 (:|value|
(p/value source ns
))
164 (:|data|
(p/data source ns
))
165 (:|externalRef|
(p/external-ref source ns
))
166 (:|grammar|
(p/grammar source ns
))
167 (t (skip-foreign source
))))))
169 (defun p/pattern
+ (source)
170 (let ((children nil
))
172 (case (klacks:peek-next source
)
174 (let ((p (p/pattern source
))) (when p
(push p children
))))
175 (:end-element
(return))))
177 (error "empty element"))
178 (nreverse children
)))
180 (defun p/pattern?
(source)
182 (case (klacks:peek-next source
)
183 (:start-element
(return (p/pattern source
)))
184 (:end-element
(return)))))
186 (defun p/element
(source name ns
)
187 (klacks:expecting-element
(source "element")
188 (let ((result (make-element :ns ns
)))
190 (setf (pattern-name result
) (list :name name
))
191 (setf (pattern-name result
) (p/name-class source
)))
192 (setf (pattern-children result
) (p/pattern
+ source
))
195 (defun p/attribute
(source name ns
)
196 (klacks:expecting-element
(source "attribute")
197 (let ((result (make-attribute :ns ns
)))
199 (setf (pattern-name result
) (list :name name
))
200 (setf (pattern-name result
) (p/name-class source
)))
201 (setf (pattern-child result
) (p/pattern? source
))
204 (defun p/combination
(constructor source ns
)
205 (klacks:expecting-element
(source)
206 (let ((possibility (p/pattern
+ source
)))
207 (funcall constructor
:possibility possibility
:ns ns
))))
209 (defun p/ref
(source ns
)
210 (klacks:expecting-element
(source "ref")
211 (make-ref :name
(ntc "name" (klacks:list-attributes source
))
214 (defun p/parent-ref
(source ns
)
215 (klacks:expecting-element
(source "parentRef")
216 (make-parent-ref :name
(ntc "name" (klacks:list-attributes source
))
219 (defun p/empty
(source ns
)
220 (klacks:expecting-element
(source "empty")
221 (klacks:consume source
)
222 (make-empty :ns ns
)))
224 (defun p/text
(source ns
)
225 (klacks:expecting-element
(source "text")
226 (klacks:consume source
)
229 (defun parse-characters (source)
233 (multiple-value-bind (key data
) (klacks:peek-next source
)
236 (setf tmp
(concatenate 'string tmp data
)))
237 (:end-element
(return)))))
240 (defun p/value
(source ns
)
241 (klacks:expecting-element
(source "value")
242 (let* ((type (ntc "type" (klacks:list-attributes source
)))
243 (string (parse-characters source
))
244 (dl *datatype-library
*))
248 (make-value :string string
:type type
:datatype-library dl
:ns ns
))))
250 (defun p/data
(source ns
)
251 (klacks:expecting-element
(source "data")
252 (let* ((type (ntc "type" (klacks:list-attributes source
)))
253 (result (make-data :type type
254 :datatype-library
*datatype-library
*
258 (multiple-value-bind (key lname
)
259 (klacks:peek-next source
)
262 (case (find-symbol lname
:keyword
)
263 (:|param|
(push (p/param source
) params
))
265 (setf (pattern-except result
) (p/except-pattern source
))
267 (t (skip-foreign source
))))
270 (setf (pattern-params result
) (nreverse params
))
273 (defun p/param
(source)
274 (klacks:expecting-element
(source "param")
275 (let ((name (ntc "name" (klacks:list-attributes source
)))
276 (string (parse-characters source
)))
277 (make-param :name name
:string string
))))
279 (defun p/except-pattern
(source)
280 (klacks:expecting-element
(source "except")
281 (with-datatype-library (klacks:list-attributes source
)
282 (p/pattern
+ source
))))
284 (defun p/not-allowed
(source ns
)
285 (klacks:expecting-element
(source "notAllowed")
286 (make-not-allowed :ns ns
)))
288 (defun p/external-ref
(source ns
)
289 (klacks:expecting-element
(source "externalRef")
291 (escape-uri (attribute "href" (klacks:list-attributes source
)))))
292 (when (find href
*include-href-stack
* :test
#'string
=)
293 (error "looping include"))
294 (let* ((*include-href-stack
* (cons href
*include-href-stack
*))
295 (xstream (cxml::xstream-open-extid
* *entity-resolver
* nil href
))
297 (klacks:with-open-source
(source (cxml:make-source xstream
))
298 (klacks:find-event source
:start-element
)
299 (let ((*datatype-library
* ""))
300 (p/pattern source
)))))
301 (unless (pattern-ns result
)
302 (setf (pattern-ns result
) ns
))
305 (defun p/grammar
(source ns
)
306 (klacks:expecting-element
(source "grammar")
307 (make-grammar :content
(p/grammar-content
* source
) :ns ns
)))
309 (defun p/grammar-content
* (source &key disallow-include
)
312 (multiple-value-bind (key lname
) (klacks:peek-next source
)
315 (with-datatype-library (klacks:list-attributes source
)
316 (case (find-symbol lname
:keyword
)
317 (:|start|
(push (p/start source
) content
))
318 (:|define|
(push (p/define source
) content
))
319 (:|div|
(push (p/div source
) content
))
321 (when disallow-include
322 (error "nested include not permitted"))
323 (push (p/include source
) content
))
324 (t (skip-foreign source
)))))
325 (:end-element
(return)))))
328 (defun p/start
(source)
329 (klacks:expecting-element
(source "start")
330 (let ((combine (ntc "combine" source
))
331 (child (p/pattern source
)))
332 (make-start :combine
(find-symbol (string-upcase combine
) :keyword
)
335 (defun p/define
(source)
336 (klacks:expecting-element
(source "define")
337 (let ((name (ntc "name" source
))
338 (combine (ntc "combine" source
))
339 (children (p/pattern
+ source
)))
340 (make-define :name name
341 :combine
(find-symbol (string-upcase combine
) :keyword
)
342 :children children
))))
344 (defun p/div
(source)
345 (klacks:expecting-element
(source "div")
346 (make-div :content
(p/grammar-content
* source
))))
348 (defun p/include
(source)
349 (klacks:expecting-element
(source "include")
351 (escape-uri (attribute "href" (klacks:list-attributes source
))))
352 (include-content (p/grammar-content
* source
:disallow-include t
)))
353 (when (find href
*include-href-stack
* :test
#'string
=)
354 (error "looping include"))
355 (let* ((*include-href-stack
* (cons href
*include-href-stack
*))
356 (xstream (cxml::xstream-open-extid
* *entity-resolver
* nil href
))
358 (klacks:with-open-source
(source (cxml:make-source xstream
))
359 (klacks:find-event source
:start-element
)
360 (let ((*datatype-library
* ""))
361 (p/grammar source
"wrong://"))))
362 (grammar-content (pattern-content grammar
)))
364 (cons (make-div :children
365 (simplify-include grammar-content
367 include-content
))))))
369 (defun simplify-include/map
(fn l
)
372 for value
= (let ((result (funcall fn x
)))
375 for x in
(div-content x
)
376 for value
= (funcall fn x
)
378 collect value into content
380 (setf (div-content x
) content
)))
385 (defun simplify-include/start
(grammar-content include-content
)
388 (simplify-include/map
(lambda (x)
389 (when (typep x
'start
)
396 (simplify-include/map
(lambda (x)
398 ((typep x
'start
) (setf ok t
) nil
)
402 (error "expected start in grammar")))
405 (defun simplify-include/define
(grammar-content include-content
)
407 (simplify-include/map
(lambda (x)
408 (when (typep x
'define
)
409 (push (cons x nil
) defines
))
413 (simplify-include/map
415 (if (typep x
'define
)
416 (let ((cons (find (define-name x
) defines
:key
#'car
)))
425 (loop for
(define . okp
) in defines do
427 (error "expected matching ~A in grammar" define
))))))
429 (defun simplify-include (grammar-content include-content
)
430 (simplify-include/define
431 (simplify-include/start grammar-content include-content
)
434 (defun p/name-class
(source)
435 (klacks:expecting-element
(source)
436 (with-datatype-library (klacks:list-attributes source
)
437 (case (find-symbol (klacks:current-lname source
) :keyword
)
439 (list :name
(string-trim *whitespace
* (parse-characters source
))))
441 (cons :any
(p/except-name-class? source
)))
443 (cons :ns
(p/except-name-class? source
)))
445 (cons :choice
(p/name-class
* source
)))
447 (skip-foreign source
))))))
449 (defun p/name-class
* (source)
452 (case (klacks:peek-next source
)
453 (:start-element
(push (p/name-class source
) results
))
454 (:end-element
(return))))
457 (defun p/except-name-class?
(source)
459 (multiple-value-bind (key lname
)
460 (klacks:peek-next source
)
461 (unless (eq key
:start-element
)
463 (when (string= (find-symbol lname
:keyword
) "except")
464 (return (p/except-name-class source
)))
465 (skip-foreign source
))))
467 (defun p/except-name-class
(source)
468 (klacks:expecting-element
(source "except")
469 (with-datatype-library (klacks:list-attributes source
)
470 (cons :except
(p/name-class source
)))))
472 (defun escape-uri (string)
473 (with-output-to-string (out)
474 (loop for c across
(cxml::rod-to-utf8-string string
) do
475 (let ((code (char-code c
)))
476 ;; http://www.w3.org/TR/xlink/#link-locators
477 (if (or (>= code
127) (<= code
32) (find c
"<>\"{}|\\^`"))
478 (format out
"%~2,'0X" code
)
479 (write-char c out
))))))
485 ;;; Foreign attributes and elements are removed implicitly while parsing.
488 ;;; All character data is discarded while parsing (which can only be
489 ;;; whitespace after validation).
491 ;;; Whitespace in name, type, and combine attributes is stripped while
492 ;;; parsing. Ditto for <name/>.
494 ;;; 4.3. datatypeLibrary attribute
495 ;;; Escaping is done by p/pattern.
496 ;;; Attribute value defaulting is done using *datatype-library*; only
497 ;;; p/data and p/value record the computed value.
499 ;;; 4.4. type attribute of value element
502 ;;; 4.5. href attribute
503 ;;; Escaping is done by p/include and p/external-ref.
505 ;;; FIXME: Mime-type handling should be the job of the entity resolver,
506 ;;; but that requires xstream hacking.
508 ;;; 4.6. externalRef element
509 ;;; Done by p/external-ref.
511 ;;; 4.7. include element
512 ;;; Done by p/include.
517 (defun test (&optional
(p "/home/david/src/lisp/cxml-rng/spec-split/*"))
518 (dribble "/home/david/src/lisp/cxml-rng/TEST")
521 (dolist (d (directory p
))
522 (let ((name (car (last (pathname-directory d
)))))
523 (when (parse-integer name
:junk-allowed t
)
525 (let* ((i (merge-pathnames "i.rng" d
))
526 (c (merge-pathnames "c.rng" d
)))
527 (format t
"~A: " name
)
535 (format t
" FAIL: ~A~%" c
)))
539 (format t
" FAIL: didn't detect invalid schema~%"))
541 (format t
" PASS: ~A~%" c
)
544 (format t
" FAIL: incorrect condition type: ~A~%" c
))))))))
545 (format t
"Passed ~D/~D tests.~%" pass total
))