summaryrefslogtreecommitdiff
path: root/lisp/nxml/rng-match.el
diff options
context:
space:
mode:
authorMark A. Hershberger <mah@everybody.org>2007-11-23 06:58:00 +0000
committerMark A. Hershberger <mah@everybody.org>2007-11-23 06:58:00 +0000
commit8cd39fb3c4cf47d2464f00eaa69c587e17dd11cc (patch)
tree7bcd47a7dcbbad100dd3e8f8a7e08b48353c58a8 /lisp/nxml/rng-match.el
parentf7cf8b2009b0bc2526d50c3455f737a543122dd4 (diff)
downloademacs-8cd39fb3c4cf47d2464f00eaa69c587e17dd11cc.tar.gz
Initial merge of nxml
Diffstat (limited to 'lisp/nxml/rng-match.el')
-rw-r--r--lisp/nxml/rng-match.el1739
1 files changed, 1739 insertions, 0 deletions
diff --git a/lisp/nxml/rng-match.el b/lisp/nxml/rng-match.el
new file mode 100644
index 00000000000..d3692615549
--- /dev/null
+++ b/lisp/nxml/rng-match.el
@@ -0,0 +1,1739 @@
+;;; rng-match.el --- matching of RELAX NG patterns against XML events
+
+;; Copyright (C) 2003 Free Software Foundation, Inc.
+
+;; Author: James Clark
+;; Keywords: XML, RelaxNG
+
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2 of
+;; the License, or (at your option) any later version.
+
+;; This program is distributed in the hope that it will be
+;; useful, but WITHOUT ANY WARRANTY; without even the implied
+;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+;; PURPOSE. See the GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public
+;; License along with this program; if not, write to the Free
+;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+;; MA 02111-1307 USA
+
+;;; Commentary:
+
+;; This uses the algorithm described in
+;; http://www.thaiopensource.com/relaxng/derivative.html
+;;
+;; The schema to be used is contained in the variable
+;; rng-current-schema. It has the form described in the file
+;; rng-pttrn.el.
+;;
+;;; Code:
+
+(require 'rng-pttrn)
+(require 'rng-util)
+(require 'rng-dt)
+
+(defvar rng-not-allowed-ipattern nil)
+(defvar rng-empty-ipattern nil)
+(defvar rng-text-ipattern nil)
+
+(defvar rng-compile-table nil)
+
+(defvar rng-being-compiled nil
+ "Contains a list of ref patterns currently being compiled.
+Used to detect illegal recursive references.")
+
+(defvar rng-ipattern-table nil)
+
+(defvar rng-last-ipattern-index nil)
+
+(defvar rng-match-state nil
+ "An ipattern representing the current state of validation.")
+
+;;; Inline functions
+
+(defsubst rng-update-match-state (new-state)
+ (if (and (eq new-state rng-not-allowed-ipattern)
+ (not (eq rng-match-state rng-not-allowed-ipattern)))
+ nil
+ (setq rng-match-state new-state)
+ t))
+
+;;; Interned patterns
+
+(eval-when-compile
+ (defun rng-ipattern-slot-accessor-name (slot-name)
+ (intern (concat "rng-ipattern-get-"
+ (symbol-name slot-name))))
+
+ (defun rng-ipattern-slot-setter-name (slot-name)
+ (intern (concat "rng-ipattern-set-"
+ (symbol-name slot-name)))))
+
+(defmacro rng-ipattern-defslot (slot-name index)
+ `(progn
+ (defsubst ,(rng-ipattern-slot-accessor-name slot-name) (ipattern)
+ (aref ipattern ,index))
+ (defsubst ,(rng-ipattern-slot-setter-name slot-name) (ipattern value)
+ (aset ipattern ,index value))))
+
+(rng-ipattern-defslot type 0)
+(rng-ipattern-defslot index 1)
+(rng-ipattern-defslot name-class 2)
+(rng-ipattern-defslot datatype 2)
+(rng-ipattern-defslot after 2)
+(rng-ipattern-defslot child 3)
+(rng-ipattern-defslot value-object 3)
+(rng-ipattern-defslot nullable 4)
+(rng-ipattern-defslot memo-text-typed 5)
+(rng-ipattern-defslot memo-map-start-tag-open-deriv 6)
+(rng-ipattern-defslot memo-map-start-attribute-deriv 7)
+(rng-ipattern-defslot memo-start-tag-close-deriv 8)
+(rng-ipattern-defslot memo-text-only-deriv 9)
+(rng-ipattern-defslot memo-mixed-text-deriv 10)
+(rng-ipattern-defslot memo-map-data-deriv 11)
+(rng-ipattern-defslot memo-end-tag-deriv 12)
+
+(defconst rng-memo-map-alist-max 10)
+
+(defsubst rng-memo-map-get (key mm)
+ "Return the value associated with KEY in memo-map MM."
+ (let ((found (assoc key mm)))
+ (if found
+ (cdr found)
+ (and mm
+ (let ((head (car mm)))
+ (and (hash-table-p head)
+ (gethash key head)))))))
+
+(defun rng-memo-map-add (key value mm &optional weakness)
+ "Associate KEY with VALUE in memo-map MM and return the new memo-map.
+The new memo-map may or may not be a different object from MM.
+
+Alists are better for small maps. Hash tables are better for large
+maps. A memo-map therefore starts off as an alist and switches to a
+hash table for large memo-maps. A memo-map is always a list. An empty
+memo-map is represented by nil. A large memo-map is represented by a
+list containing just a hash-table. A small memo map is represented by
+a list whose cdr is an alist and whose car is the number of entries in
+the alist. The complete memo-map can be passed to assoc without
+problems: assoc ignores any members that are not cons cells. There is
+therefore minimal overhead in successful lookups on small lists
+\(which is the most common case)."
+ (if (null mm)
+ (list 1 (cons key value))
+ (let ((head (car mm)))
+ (cond ((hash-table-p head)
+ (puthash key value head)
+ mm)
+ ((>= head rng-memo-map-alist-max)
+ (let ((ht (make-hash-table :test 'equal
+ :weakness weakness
+ :size (* 2 rng-memo-map-alist-max))))
+ (setq mm (cdr mm))
+ (while mm
+ (setq head (car mm))
+ (puthash (car head) (cdr head) ht)
+ (setq mm (cdr mm)))
+ (cons ht nil)))
+ (t (cons (1+ head)
+ (cons (cons key value)
+ (cdr mm))))))))
+
+(defsubst rng-make-ipattern (type index name-class child nullable)
+ (vector type index name-class child nullable
+ ;; 5 memo-text-typed
+ 'unknown
+ ;; 6 memo-map-start-tag-open-deriv
+ nil
+ ;; 7 memo-map-start-attribute-deriv
+ nil
+ ;; 8 memo-start-tag-close-deriv
+ nil
+ ;; 9 memo-text-only-deriv
+ nil
+ ;; 10 memo-mixed-text-deriv
+ nil
+ ;; 11 memo-map-data-deriv
+ nil
+ ;; 12 memo-end-tag-deriv
+ nil))
+
+(defun rng-ipattern-maybe-init ()
+ (unless rng-ipattern-table
+ (setq rng-ipattern-table (make-hash-table :test 'equal))
+ (setq rng-last-ipattern-index -1)))
+
+(defun rng-ipattern-clear ()
+ (when rng-ipattern-table
+ (clrhash rng-ipattern-table))
+ (setq rng-last-ipattern-index -1))
+
+(defsubst rng-gen-ipattern-index ()
+ (setq rng-last-ipattern-index (1+ rng-last-ipattern-index)))
+
+(defun rng-put-ipattern (key type name-class child nullable)
+ (let ((ipattern
+ (rng-make-ipattern type
+ (rng-gen-ipattern-index)
+ name-class
+ child
+ nullable)))
+ (puthash key ipattern rng-ipattern-table)
+ ipattern))
+
+(defun rng-get-ipattern (key)
+ (gethash key rng-ipattern-table))
+
+(or rng-not-allowed-ipattern
+ (setq rng-not-allowed-ipattern
+ (rng-make-ipattern 'not-allowed -3 nil nil nil)))
+
+(or rng-empty-ipattern
+ (setq rng-empty-ipattern
+ (rng-make-ipattern 'empty -2 nil nil t)))
+
+(or rng-text-ipattern
+ (setq rng-text-ipattern
+ (rng-make-ipattern 'text -1 nil nil t)))
+
+(defconst rng-const-ipatterns
+ (list rng-not-allowed-ipattern
+ rng-empty-ipattern
+ rng-text-ipattern))
+
+(defun rng-intern-after (child after)
+ (if (eq child rng-not-allowed-ipattern)
+ rng-not-allowed-ipattern
+ (let ((key (list 'after
+ (rng-ipattern-get-index child)
+ (rng-ipattern-get-index after))))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'after
+ after
+ child
+ nil)))))
+
+(defun rng-intern-attribute (name-class ipattern)
+ (if (eq ipattern rng-not-allowed-ipattern)
+ rng-not-allowed-ipattern
+ (let ((key (list 'attribute
+ name-class
+ (rng-ipattern-get-index ipattern))))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'attribute
+ name-class
+ ipattern
+ nil)))))
+
+(defun rng-intern-data (dt matches-anything)
+ (let ((key (list 'data dt)))
+ (or (rng-get-ipattern key)
+ (let ((ipattern (rng-put-ipattern key
+ 'data
+ dt
+ nil
+ matches-anything)))
+ (rng-ipattern-set-memo-text-typed ipattern
+ (not matches-anything))
+ ipattern))))
+
+(defun rng-intern-data-except (dt ipattern)
+ (let ((key (list 'data-except dt ipattern)))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'data-except
+ dt
+ ipattern
+ nil))))
+
+(defun rng-intern-value (dt obj)
+ (let ((key (list 'value dt obj)))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'value
+ dt
+ obj
+ nil))))
+
+(defun rng-intern-one-or-more (ipattern)
+ (or (rng-intern-one-or-more-shortcut ipattern)
+ (let ((key (cons 'one-or-more
+ (list (rng-ipattern-get-index ipattern)))))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'one-or-more
+ nil
+ ipattern
+ (rng-ipattern-get-nullable ipattern))))))
+
+(defun rng-intern-one-or-more-shortcut (ipattern)
+ (cond ((eq ipattern rng-not-allowed-ipattern)
+ rng-not-allowed-ipattern)
+ ((eq ipattern rng-empty-ipattern)
+ rng-empty-ipattern)
+ ((eq (rng-ipattern-get-type ipattern) 'one-or-more)
+ ipattern)
+ (t nil)))
+
+(defun rng-intern-list (ipattern)
+ (if (eq ipattern rng-not-allowed-ipattern)
+ rng-not-allowed-ipattern
+ (let ((key (cons 'list
+ (list (rng-ipattern-get-index ipattern)))))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'list
+ nil
+ ipattern
+ nil)))))
+
+(defun rng-intern-group (ipatterns)
+ "Return a ipattern for the list of group members in IPATTERNS."
+ (or (rng-intern-group-shortcut ipatterns)
+ (let* ((tem (rng-normalize-group-list ipatterns))
+ (normalized (cdr tem)))
+ (or (rng-intern-group-shortcut normalized)
+ (let ((key (cons 'group
+ (mapcar 'rng-ipattern-get-index normalized))))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'group
+ nil
+ normalized
+ (car tem))))))))
+
+(defun rng-intern-group-shortcut (ipatterns)
+ "Try to shortcut interning a group list. If successful, return the
+interned pattern. Otherwise return nil."
+ (while (and ipatterns
+ (eq (car ipatterns) rng-empty-ipattern))
+ (setq ipatterns (cdr ipatterns)))
+ (if ipatterns
+ (let ((ret (car ipatterns)))
+ (if (eq ret rng-not-allowed-ipattern)
+ rng-not-allowed-ipattern
+ (setq ipatterns (cdr ipatterns))
+ (while (and ipatterns ret)
+ (let ((tem (car ipatterns)))
+ (cond ((eq tem rng-not-allowed-ipattern)
+ (setq ret tem)
+ (setq ipatterns nil))
+ ((eq tem rng-empty-ipattern)
+ (setq ipatterns (cdr ipatterns)))
+ (t
+ ;; Stop here rather than continuing
+ ;; looking for not-allowed patterns.
+ ;; We do a complete scan elsewhere.
+ (setq ret nil)))))
+ ret))
+ rng-empty-ipattern))
+
+(defun rng-normalize-group-list (ipatterns)
+ "Normalize a list containing members of a group.
+Expands nested groups, removes empty members, handles notAllowed.
+Returns a pair whose car says whether the list is nullable and whose
+cdr is the normalized list."
+ (let ((nullable t)
+ (result nil)
+ member)
+ (while ipatterns
+ (setq member (car ipatterns))
+ (setq ipatterns (cdr ipatterns))
+ (when nullable
+ (setq nullable (rng-ipattern-get-nullable member)))
+ (cond ((eq (rng-ipattern-get-type member) 'group)
+ (setq result
+ (nconc (reverse (rng-ipattern-get-child member))
+ result)))
+ ((eq member rng-not-allowed-ipattern)
+ (setq result (list rng-not-allowed-ipattern))
+ (setq ipatterns nil))
+ ((not (eq member rng-empty-ipattern))
+ (setq result (cons member result)))))
+ (cons nullable (nreverse result))))
+
+(defun rng-intern-interleave (ipatterns)
+ (or (rng-intern-group-shortcut ipatterns)
+ (let* ((tem (rng-normalize-interleave-list ipatterns))
+ (normalized (cdr tem)))
+ (or (rng-intern-group-shortcut normalized)
+ (let ((key (cons 'interleave
+ (mapcar 'rng-ipattern-get-index normalized))))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'interleave
+ nil
+ normalized
+ (car tem))))))))
+
+(defun rng-normalize-interleave-list (ipatterns)
+ "Normalize a list containing members of an interleave.
+Expands nested groups, removes empty members, handles notAllowed.
+Returns a pair whose car says whether the list is nullable and whose
+cdr is the normalized list."
+ (let ((nullable t)
+ (result nil)
+ member)
+ (while ipatterns
+ (setq member (car ipatterns))
+ (setq ipatterns (cdr ipatterns))
+ (when nullable
+ (setq nullable (rng-ipattern-get-nullable member)))
+ (cond ((eq (rng-ipattern-get-type member) 'interleave)
+ (setq result
+ (append (rng-ipattern-get-child member)
+ result)))
+ ((eq member rng-not-allowed-ipattern)
+ (setq result (list rng-not-allowed-ipattern))
+ (setq ipatterns nil))
+ ((not (eq member rng-empty-ipattern))
+ (setq result (cons member result)))))
+ (cons nullable (sort result 'rng-compare-ipattern))))
+
+;; Would be cleaner if this didn't modify IPATTERNS.
+
+(defun rng-intern-choice (ipatterns)
+ "Return a choice ipattern for the list of choices in IPATTERNS.
+May alter IPATTERNS."
+ (or (rng-intern-choice-shortcut ipatterns)
+ (let* ((tem (rng-normalize-choice-list ipatterns))
+ (normalized (cdr tem)))
+ (or (rng-intern-choice-shortcut normalized)
+ (rng-intern-choice1 normalized (car tem))))))
+
+(defun rng-intern-optional (ipattern)
+ (cond ((rng-ipattern-get-nullable ipattern) ipattern)
+ ((eq ipattern rng-not-allowed-ipattern) rng-empty-ipattern)
+ (t (rng-intern-choice1
+ ;; This is sorted since the empty pattern
+ ;; is before everything except not allowed.
+ ;; It cannot have a duplicate empty pattern,
+ ;; since it is not nullable.
+ (cons rng-empty-ipattern
+ (if (eq (rng-ipattern-get-type ipattern) 'choice)
+ (rng-ipattern-get-child ipattern)
+ (list ipattern)))
+ t))))
+
+
+(defun rng-intern-choice1 (normalized nullable)
+ (let ((key (cons 'choice
+ (mapcar 'rng-ipattern-get-index normalized))))
+ (or (rng-get-ipattern key)
+ (rng-put-ipattern key
+ 'choice
+ nil
+ normalized
+ nullable))))
+
+(defun rng-intern-choice-shortcut (ipatterns)
+ "Try to shortcut interning a choice list. If successful, return the
+interned pattern. Otherwise return nil."
+ (while (and ipatterns
+ (eq (car ipatterns)
+ rng-not-allowed-ipattern))
+ (setq ipatterns (cdr ipatterns)))
+ (if ipatterns
+ (let ((ret (car ipatterns)))
+ (setq ipatterns (cdr ipatterns))
+ (while (and ipatterns ret)
+ (or (eq (car ipatterns) rng-not-allowed-ipattern)
+ (eq (car ipatterns) ret)
+ (setq ret nil))
+ (setq ipatterns (cdr ipatterns)))
+ ret)
+ rng-not-allowed-ipattern))
+
+(defun rng-normalize-choice-list (ipatterns)
+ "Normalize a list of choices, expanding nested choices, removing
+not-allowed members, sorting by index and removing duplicates. Return
+a pair whose car says whether the list is nullable and whose cdr is
+the normalized list."
+ (let ((sorted t)
+ (nullable nil)
+ (head (cons nil ipatterns)))
+ (let ((tail head)
+ (final-tail nil)
+ (prev-index -100)
+ (cur ipatterns)
+ member)
+ ;; the cdr of tail is always cur
+ (while cur
+ (setq member (car cur))
+ (or nullable
+ (setq nullable (rng-ipattern-get-nullable member)))
+ (cond ((eq (rng-ipattern-get-type member) 'choice)
+ (setq final-tail
+ (append (rng-ipattern-get-child member)
+ final-tail))
+ (setq cur (cdr cur))
+ (setq sorted nil)
+ (setcdr tail cur))
+ ((eq member rng-not-allowed-ipattern)
+ (setq cur (cdr cur))
+ (setcdr tail cur))
+ (t
+ (if (and sorted
+ (let ((cur-index (rng-ipattern-get-index member)))
+ (if (>= prev-index cur-index)
+ (or (= prev-index cur-index) ; will remove it
+ (setq sorted nil)) ; won't remove it
+ (setq prev-index cur-index)
+ ;; won't remove it
+ nil)))
+ (progn
+ ;; remove it
+ (setq cur (cdr cur))
+ (setcdr tail cur))
+ ;; don't remove it
+ (setq tail cur)
+ (setq cur (cdr cur))))))
+ (setcdr tail final-tail))
+ (setq head (cdr head))
+ (cons nullable
+ (if sorted
+ head
+ (rng-uniquify-eq (sort head 'rng-compare-ipattern))))))
+
+(defun rng-compare-ipattern (p1 p2)
+ (< (rng-ipattern-get-index p1)
+ (rng-ipattern-get-index p2)))
+
+;;; Name classes
+
+(defsubst rng-name-class-contains (nc nm)
+ (if (consp nc)
+ (equal nm nc)
+ (rng-name-class-contains1 nc nm)))
+
+(defun rng-name-class-contains1 (nc nm)
+ (let ((type (aref nc 0)))
+ (cond ((eq type 'any-name) t)
+ ((eq type 'any-name-except)
+ (not (rng-name-class-contains (aref nc 1) nm)))
+ ((eq type 'ns-name)
+ (eq (car nm) (aref nc 1)))
+ ((eq type 'ns-name-except)
+ (and (eq (car nm) (aref nc 1))
+ (not (rng-name-class-contains (aref nc 2) nm))))
+ ((eq type 'choice)
+ (let ((choices (aref nc 1))
+ (ret nil))
+ (while choices
+ (if (rng-name-class-contains (car choices) nm)
+ (progn
+ (setq choices nil)
+ (setq ret t))
+ (setq choices (cdr choices))))
+ ret)))))
+
+(defun rng-name-class-possible-names (nc accum)
+ "Return a list of possible names that nameclass NC can match.
+
+Each possible name should be returned as a (NAMESPACE . LOCAL-NAME)
+pair, where NAMESPACE is a symbol or nil and LOCAL-NAME is a string.
+nil for NAMESPACE matches the absent namespace. ACCUM is a list of
+names which should be appended to the returned list. The returned list
+may contain duplicates."
+ (if (consp nc)
+ (cons nc accum)
+ (when (eq (aref nc 0) 'choice)
+ (let ((members (aref nc 1)) member)
+ (while members
+ (setq member (car members))
+ (setq accum
+ (if (consp member)
+ (cons member accum)
+ (rng-name-class-possible-names member
+ accum)))
+ (setq members (cdr members)))))
+ accum))
+
+;;; Debugging utilities
+
+(defun rng-ipattern-to-string (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'after)
+ (concat (rng-ipattern-to-string
+ (rng-ipattern-get-child ipattern))
+ " </> "
+ (rng-ipattern-to-string
+ (rng-ipattern-get-after ipattern))))
+ ((eq type 'element)
+ (concat "element "
+ (rng-name-class-to-string
+ (rng-ipattern-get-name-class ipattern))
+ ;; we can get cycles with elements so don't print it out
+ " {...}"))
+ ((eq type 'attribute)
+ (concat "attribute "
+ (rng-name-class-to-string
+ (rng-ipattern-get-name-class ipattern))
+ " { "
+ (rng-ipattern-to-string
+ (rng-ipattern-get-child ipattern))
+ " } "))
+ ((eq type 'empty) "empty")
+ ((eq type 'text) "text")
+ ((eq type 'not-allowed) "notAllowed")
+ ((eq type 'one-or-more)
+ (concat (rng-ipattern-to-string
+ (rng-ipattern-get-child ipattern))
+ "+"))
+ ((eq type 'choice)
+ (concat "("
+ (mapconcat 'rng-ipattern-to-string
+ (rng-ipattern-get-child ipattern)
+ " | ")
+ ")"))
+ ((eq type 'group)
+ (concat "("
+ (mapconcat 'rng-ipattern-to-string
+ (rng-ipattern-get-child ipattern)
+ ", ")
+ ")"))
+ ((eq type 'interleave)
+ (concat "("
+ (mapconcat 'rng-ipattern-to-string
+ (rng-ipattern-get-child ipattern)
+ " & ")
+ ")"))
+ (t (symbol-name type)))))
+
+(defun rng-name-class-to-string (nc)
+ (if (consp nc)
+ (cdr nc)
+ (let ((type (aref nc 0)))
+ (cond ((eq type 'choice)
+ (mapconcat 'rng-name-class-to-string
+ (aref nc 1)
+ "|"))
+ (t (concat (symbol-name type) "*"))))))
+
+
+;;; Compiling
+
+(defun rng-compile-maybe-init ()
+ (unless rng-compile-table
+ (setq rng-compile-table (make-hash-table :test 'eq))))
+
+(defun rng-compile-clear ()
+ (when rng-compile-table
+ (clrhash rng-compile-table)))
+
+(defun rng-compile (pattern)
+ (or (gethash pattern rng-compile-table)
+ (let ((ipattern (apply (get (car pattern) 'rng-compile)
+ (cdr pattern))))
+ (puthash pattern ipattern rng-compile-table)
+ ipattern)))
+
+(put 'empty 'rng-compile 'rng-compile-empty)
+(put 'text 'rng-compile 'rng-compile-text)
+(put 'not-allowed 'rng-compile 'rng-compile-not-allowed)
+(put 'element 'rng-compile 'rng-compile-element)
+(put 'attribute 'rng-compile 'rng-compile-attribute)
+(put 'choice 'rng-compile 'rng-compile-choice)
+(put 'optional 'rng-compile 'rng-compile-optional)
+(put 'group 'rng-compile 'rng-compile-group)
+(put 'interleave 'rng-compile 'rng-compile-interleave)
+(put 'ref 'rng-compile 'rng-compile-ref)
+(put 'one-or-more 'rng-compile 'rng-compile-one-or-more)
+(put 'zero-or-more 'rng-compile 'rng-compile-zero-or-more)
+(put 'mixed 'rng-compile 'rng-compile-mixed)
+(put 'data 'rng-compile 'rng-compile-data)
+(put 'data-except 'rng-compile 'rng-compile-data-except)
+(put 'value 'rng-compile 'rng-compile-value)
+(put 'list 'rng-compile 'rng-compile-list)
+
+(defun rng-compile-not-allowed () rng-not-allowed-ipattern)
+(defun rng-compile-empty () rng-empty-ipattern)
+(defun rng-compile-text () rng-text-ipattern)
+
+(defun rng-compile-element (name-class pattern)
+ ;; don't intern
+ (rng-make-ipattern 'element
+ (rng-gen-ipattern-index)
+ (rng-compile-name-class name-class)
+ pattern ; compile lazily
+ nil))
+
+(defun rng-element-get-child (element)
+ (let ((tem (rng-ipattern-get-child element)))
+ (if (vectorp tem)
+ tem
+ (rng-ipattern-set-child element (rng-compile tem)))))
+
+(defun rng-compile-attribute (name-class pattern)
+ (rng-intern-attribute (rng-compile-name-class name-class)
+ (rng-compile pattern)))
+
+(defun rng-compile-ref (pattern name)
+ (and (memq pattern rng-being-compiled)
+ (rng-compile-error "Reference loop on symbol %s" name))
+ (setq rng-being-compiled
+ (cons pattern rng-being-compiled))
+ (unwind-protect
+ (rng-compile pattern)
+ (setq rng-being-compiled
+ (cdr rng-being-compiled))))
+
+(defun rng-compile-one-or-more (pattern)
+ (rng-intern-one-or-more (rng-compile pattern)))
+
+(defun rng-compile-zero-or-more (pattern)
+ (rng-intern-optional
+ (rng-intern-one-or-more (rng-compile pattern))))
+
+(defun rng-compile-optional (pattern)
+ (rng-intern-optional (rng-compile pattern)))
+
+(defun rng-compile-mixed (pattern)
+ (rng-intern-interleave (cons rng-text-ipattern
+ (list (rng-compile pattern)))))
+
+(defun rng-compile-list (pattern)
+ (rng-intern-list (rng-compile pattern)))
+
+(defun rng-compile-choice (&rest patterns)
+ (rng-intern-choice (mapcar 'rng-compile patterns)))
+
+(defun rng-compile-group (&rest patterns)
+ (rng-intern-group (mapcar 'rng-compile patterns)))
+
+(defun rng-compile-interleave (&rest patterns)
+ (rng-intern-interleave (mapcar 'rng-compile patterns)))
+
+(defun rng-compile-dt (name params)
+ (let ((rng-dt-error-reporter 'rng-compile-error))
+ (funcall (let ((uri (car name)))
+ (or (get uri 'rng-dt-compile)
+ (rng-compile-error "Unknown datatype library %s" uri)))
+ (cdr name)
+ params)))
+
+(defun rng-compile-data (name params)
+ (let ((dt (rng-compile-dt name params)))
+ (rng-intern-data (cdr dt) (car dt))))
+
+(defun rng-compile-data-except (name params pattern)
+ (rng-intern-data-except (cdr (rng-compile-dt name params))
+ (rng-compile pattern)))
+
+(defun rng-compile-value (name str context)
+ (let* ((dt (cdr (rng-compile-dt name '())))
+ (rng-dt-namespace-context-getter (list 'identity context))
+ (obj (rng-dt-make-value dt str)))
+ (if obj
+ (rng-intern-value dt obj)
+ (rng-compile-error "Value %s is not a valid instance of the datatype %s"
+ str
+ name))))
+
+(defun rng-compile-name-class (nc)
+ (let ((type (car nc)))
+ (cond ((eq type 'name) (nth 1 nc))
+ ((eq type 'any-name) [any-name])
+ ((eq type 'any-name-except)
+ (vector 'any-name-except
+ (rng-compile-name-class (nth 1 nc))))
+ ((eq type 'ns-name)
+ (vector 'ns-name (nth 1 nc)))
+ ((eq type 'ns-name-except)
+ (vector 'ns-name-except
+ (nth 1 nc)
+ (rng-compile-name-class (nth 2 nc))))
+ ((eq type 'choice)
+ (vector 'choice
+ (mapcar 'rng-compile-name-class (cdr nc))))
+ (t (error "Bad name-class type %s" type)))))
+
+;;; Searching patterns
+
+;; We write this non-recursively to avoid hitting max-lisp-eval-depth
+;; on large schemas.
+
+(defun rng-map-element-attribute (function pattern accum &rest args)
+ (let ((searched (make-hash-table :test 'eq))
+ type todo patterns)
+ (while (progn
+ (setq type (car pattern))
+ (cond ((memq type '(element attribute))
+ (setq accum
+ (apply function
+ (cons pattern
+ (cons accum args))))
+ (setq pattern (nth 2 pattern)))
+ ((eq type 'ref)
+ (setq pattern (nth 1 pattern))
+ (if (gethash pattern searched)
+ (setq pattern nil)
+ (puthash pattern t searched)))
+ ((memq type '(choice group interleave))
+ (setq todo (cons (cdr pattern) todo))
+ (setq pattern nil))
+ ((memq type '(one-or-more
+ zero-or-more
+ optional
+ mixed))
+ (setq pattern (nth 1 pattern)))
+ (t (setq pattern nil)))
+ (cond (pattern)
+ (patterns
+ (setq pattern (car patterns))
+ (setq patterns (cdr patterns))
+ t)
+ (todo
+ (setq patterns (car todo))
+ (setq todo (cdr todo))
+ (setq pattern (car patterns))
+ (setq patterns (cdr patterns))
+ t))))
+ accum))
+
+(defun rng-find-element-content-pattern (pattern accum name)
+ (if (and (eq (car pattern) 'element)
+ (rng-search-name name (nth 1 pattern)))
+ (cons (rng-compile (nth 2 pattern)) accum)
+ accum))
+
+(defun rng-search-name (name nc)
+ (let ((type (car nc)))
+ (cond ((eq type 'name)
+ (equal (cadr nc) name))
+ ((eq type 'choice)
+ (let ((choices (cdr nc))
+ (found nil))
+ (while (and choices (not found))
+ (if (rng-search-name name (car choices))
+ (setq found t)
+ (setq choices (cdr choices))))
+ found))
+ (t nil))))
+
+(defun rng-find-name-class-uris (nc accum)
+ (let ((type (car nc)))
+ (cond ((eq type 'name)
+ (rng-accum-namespace-uri (car (nth 1 nc)) accum))
+ ((memq type '(ns-name ns-name-except))
+ (rng-accum-namespace-uri (nth 1 nc) accum))
+ ((eq type 'choice)
+ (let ((choices (cdr nc)))
+ (while choices
+ (setq accum
+ (rng-find-name-class-uris (car choices) accum))
+ (setq choices (cdr choices))))
+ accum)
+ (t accum))))
+
+(defun rng-accum-namespace-uri (ns accum)
+ (if (and ns (not (memq ns accum)))
+ (cons ns accum)
+ accum))
+
+;;; Derivatives
+
+(defun rng-ipattern-text-typed-p (ipattern)
+ (let ((memo (rng-ipattern-get-memo-text-typed ipattern)))
+ (if (eq memo 'unknown)
+ (rng-ipattern-set-memo-text-typed
+ ipattern
+ (rng-ipattern-compute-text-typed-p ipattern))
+ memo)))
+
+(defun rng-ipattern-compute-text-typed-p (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'choice)
+ (let ((cur (rng-ipattern-get-child ipattern))
+ (ret nil))
+ (while (and cur (not ret))
+ (if (rng-ipattern-text-typed-p (car cur))
+ (setq ret t)
+ (setq cur (cdr cur))))
+ ret))
+ ((eq type 'group)
+ (let ((cur (rng-ipattern-get-child ipattern))
+ (ret nil)
+ member)
+ (while (and cur (not ret))
+ (setq member (car cur))
+ (if (rng-ipattern-text-typed-p member)
+ (setq ret t))
+ (setq cur
+ (and (rng-ipattern-get-nullable member)
+ (cdr cur))))
+ ret))
+ ((eq type 'after)
+ (rng-ipattern-text-typed-p (rng-ipattern-get-child ipattern)))
+ (t (and (memq type '(value list data data-except)) t)))))
+
+(defun rng-start-tag-open-deriv (ipattern nm)
+ (or (rng-memo-map-get
+ nm
+ (rng-ipattern-get-memo-map-start-tag-open-deriv ipattern))
+ (rng-ipattern-memo-start-tag-open-deriv
+ ipattern
+ nm
+ (rng-compute-start-tag-open-deriv ipattern nm))))
+
+(defun rng-ipattern-memo-start-tag-open-deriv (ipattern nm deriv)
+ (or (memq ipattern rng-const-ipatterns)
+ (rng-ipattern-set-memo-map-start-tag-open-deriv
+ ipattern
+ (rng-memo-map-add nm
+ deriv
+ (rng-ipattern-get-memo-map-start-tag-open-deriv
+ ipattern))))
+ deriv)
+
+(defun rng-compute-start-tag-open-deriv (ipattern nm)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'choice)
+ (rng-transform-choice `(lambda (p)
+ (rng-start-tag-open-deriv p ',nm))
+ ipattern))
+ ((eq type 'element)
+ (if (rng-name-class-contains
+ (rng-ipattern-get-name-class ipattern)
+ nm)
+ (rng-intern-after (rng-element-get-child ipattern)
+ rng-empty-ipattern)
+ rng-not-allowed-ipattern))
+ ((eq type 'group)
+ (rng-transform-group-nullable
+ `(lambda (p) (rng-start-tag-open-deriv p ',nm))
+ 'rng-cons-group-after
+ ipattern))
+ ((eq type 'interleave)
+ (rng-transform-interleave-single
+ `(lambda (p) (rng-start-tag-open-deriv p ',nm))
+ 'rng-subst-interleave-after
+ ipattern))
+ ((eq type 'one-or-more)
+ (rng-apply-after
+ `(lambda (p)
+ (rng-intern-group (list p ,(rng-intern-optional ipattern))))
+ (rng-start-tag-open-deriv (rng-ipattern-get-child ipattern)
+ nm)))
+ ((eq type 'after)
+ (rng-apply-after
+ `(lambda (p)
+ (rng-intern-after p
+ ,(rng-ipattern-get-after ipattern)))
+ (rng-start-tag-open-deriv (rng-ipattern-get-child ipattern)
+ nm)))
+ (t rng-not-allowed-ipattern))))
+
+(defun rng-start-attribute-deriv (ipattern nm)
+ (or (rng-memo-map-get
+ nm
+ (rng-ipattern-get-memo-map-start-attribute-deriv ipattern))
+ (rng-ipattern-memo-start-attribute-deriv
+ ipattern
+ nm
+ (rng-compute-start-attribute-deriv ipattern nm))))
+
+(defun rng-ipattern-memo-start-attribute-deriv (ipattern nm deriv)
+ (or (memq ipattern rng-const-ipatterns)
+ (rng-ipattern-set-memo-map-start-attribute-deriv
+ ipattern
+ (rng-memo-map-add
+ nm
+ deriv
+ (rng-ipattern-get-memo-map-start-attribute-deriv ipattern))))
+ deriv)
+
+(defun rng-compute-start-attribute-deriv (ipattern nm)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'choice)
+ (rng-transform-choice `(lambda (p)
+ (rng-start-attribute-deriv p ',nm))
+ ipattern))
+ ((eq type 'attribute)
+ (if (rng-name-class-contains
+ (rng-ipattern-get-name-class ipattern)
+ nm)
+ (rng-intern-after (rng-ipattern-get-child ipattern)
+ rng-empty-ipattern)
+ rng-not-allowed-ipattern))
+ ((eq type 'group)
+ (rng-transform-interleave-single
+ `(lambda (p) (rng-start-attribute-deriv p ',nm))
+ 'rng-subst-group-after
+ ipattern))
+ ((eq type 'interleave)
+ (rng-transform-interleave-single
+ `(lambda (p) (rng-start-attribute-deriv p ',nm))
+ 'rng-subst-interleave-after
+ ipattern))
+ ((eq type 'one-or-more)
+ (rng-apply-after
+ `(lambda (p)
+ (rng-intern-group (list p ,(rng-intern-optional ipattern))))
+ (rng-start-attribute-deriv (rng-ipattern-get-child ipattern)
+ nm)))
+ ((eq type 'after)
+ (rng-apply-after
+ `(lambda (p)
+ (rng-intern-after p ,(rng-ipattern-get-after ipattern)))
+ (rng-start-attribute-deriv (rng-ipattern-get-child ipattern)
+ nm)))
+ (t rng-not-allowed-ipattern))))
+
+(defun rng-cons-group-after (x y)
+ (rng-apply-after `(lambda (p) (rng-intern-group (cons p ',y)))
+ x))
+
+(defun rng-subst-group-after (new old list)
+ (rng-apply-after `(lambda (p)
+ (rng-intern-group (rng-substq p ,old ',list)))
+ new))
+
+(defun rng-subst-interleave-after (new old list)
+ (rng-apply-after `(lambda (p)
+ (rng-intern-interleave (rng-substq p ,old ',list)))
+ new))
+
+(defun rng-apply-after (f ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'after)
+ (rng-intern-after
+ (rng-ipattern-get-child ipattern)
+ (funcall f
+ (rng-ipattern-get-after ipattern))))
+ ((eq type 'choice)
+ (rng-transform-choice `(lambda (x) (rng-apply-after ,f x))
+ ipattern))
+ (t rng-not-allowed-ipattern))))
+
+(defun rng-start-tag-close-deriv (ipattern)
+ (or (rng-ipattern-get-memo-start-tag-close-deriv ipattern)
+ (rng-ipattern-set-memo-start-tag-close-deriv
+ ipattern
+ (rng-compute-start-tag-close-deriv ipattern))))
+
+(defconst rng-transform-map
+ '((choice . rng-transform-choice)
+ (group . rng-transform-group)
+ (interleave . rng-transform-interleave)
+ (one-or-more . rng-transform-one-or-more)
+ (after . rng-transform-after-child)))
+
+(defun rng-compute-start-tag-close-deriv (ipattern)
+ (let* ((type (rng-ipattern-get-type ipattern)))
+ (if (eq type 'attribute)
+ rng-not-allowed-ipattern
+ (let ((transform (assq type rng-transform-map)))
+ (if transform
+ (funcall (cdr transform)
+ 'rng-start-tag-close-deriv
+ ipattern)
+ ipattern)))))
+
+(defun rng-ignore-attributes-deriv (ipattern)
+ (let* ((type (rng-ipattern-get-type ipattern)))
+ (if (eq type 'attribute)
+ rng-empty-ipattern
+ (let ((transform (assq type rng-transform-map)))
+ (if transform
+ (funcall (cdr transform)
+ 'rng-ignore-attributes-deriv
+ ipattern)
+ ipattern)))))
+
+(defun rng-text-only-deriv (ipattern)
+ (or (rng-ipattern-get-memo-text-only-deriv ipattern)
+ (rng-ipattern-set-memo-text-only-deriv
+ ipattern
+ (rng-compute-text-only-deriv ipattern))))
+
+(defun rng-compute-text-only-deriv (ipattern)
+ (let* ((type (rng-ipattern-get-type ipattern)))
+ (if (eq type 'element)
+ rng-not-allowed-ipattern
+ (let ((transform (assq type
+ '((choice . rng-transform-choice)
+ (group . rng-transform-group)
+ (interleave . rng-transform-interleave)
+ (one-or-more . rng-transform-one-or-more)
+ (after . rng-transform-after-child)))))
+ (if transform
+ (funcall (cdr transform)
+ 'rng-text-only-deriv
+ ipattern)
+ ipattern)))))
+
+(defun rng-mixed-text-deriv (ipattern)
+ (or (rng-ipattern-get-memo-mixed-text-deriv ipattern)
+ (rng-ipattern-set-memo-mixed-text-deriv
+ ipattern
+ (rng-compute-mixed-text-deriv ipattern))))
+
+(defun rng-compute-mixed-text-deriv (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'text) ipattern)
+ ((eq type 'after)
+ (rng-transform-after-child 'rng-mixed-text-deriv
+ ipattern))
+ ((eq type 'choice)
+ (rng-transform-choice 'rng-mixed-text-deriv
+ ipattern))
+ ((eq type 'one-or-more)
+ (rng-intern-group
+ (list (rng-mixed-text-deriv
+ (rng-ipattern-get-child ipattern))
+ (rng-intern-optional ipattern))))
+ ((eq type 'group)
+ (rng-transform-group-nullable
+ 'rng-mixed-text-deriv
+ (lambda (x y) (rng-intern-group (cons x y)))
+ ipattern))
+ ((eq type 'interleave)
+ (rng-transform-interleave-single
+ 'rng-mixed-text-deriv
+ (lambda (new old list) (rng-intern-interleave
+ (rng-substq new old list)))
+ ipattern))
+ ((and (eq type 'data)
+ (not (rng-ipattern-get-memo-text-typed ipattern)))
+ ipattern)
+ (t rng-not-allowed-ipattern))))
+
+(defun rng-end-tag-deriv (ipattern)
+ (or (rng-ipattern-get-memo-end-tag-deriv ipattern)
+ (rng-ipattern-set-memo-end-tag-deriv
+ ipattern
+ (rng-compute-end-tag-deriv ipattern))))
+
+(defun rng-compute-end-tag-deriv (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'choice)
+ (rng-intern-choice
+ (mapcar 'rng-end-tag-deriv
+ (rng-ipattern-get-child ipattern))))
+ ((eq type 'after)
+ (if (rng-ipattern-get-nullable
+ (rng-ipattern-get-child ipattern))
+ (rng-ipattern-get-after ipattern)
+ rng-not-allowed-ipattern))
+ (t rng-not-allowed-ipattern))))
+
+(defun rng-data-deriv (ipattern value)
+ (or (rng-memo-map-get value
+ (rng-ipattern-get-memo-map-data-deriv ipattern))
+ (and (rng-memo-map-get
+ (cons value (rng-namespace-context-get-no-trace))
+ (rng-ipattern-get-memo-map-data-deriv ipattern))
+ (rng-memo-map-get
+ (cons value (apply (car rng-dt-namespace-context-getter)
+ (cdr rng-dt-namespace-context-getter)))
+ (rng-ipattern-get-memo-map-data-deriv ipattern)))
+ (let* ((used-context (vector nil))
+ (rng-dt-namespace-context-getter
+ (cons 'rng-namespace-context-tracer
+ (cons used-context
+ rng-dt-namespace-context-getter)))
+ (deriv (rng-compute-data-deriv ipattern value)))
+ (rng-ipattern-memo-data-deriv ipattern
+ value
+ (aref used-context 0)
+ deriv))))
+
+(defun rng-namespace-context-tracer (used getter &rest args)
+ (let ((context (apply getter args)))
+ (aset used 0 context)
+ context))
+
+(defun rng-namespace-context-get-no-trace ()
+ (let ((tem rng-dt-namespace-context-getter))
+ (while (and tem (eq (car tem) 'rng-namespace-context-tracer))
+ (setq tem (cddr tem)))
+ (apply (car tem) (cdr tem))))
+
+(defconst rng-memo-data-deriv-max-length 80
+ "Don't memoize data-derivs for values longer than this.")
+
+(defun rng-ipattern-memo-data-deriv (ipattern value context deriv)
+ (or (memq ipattern rng-const-ipatterns)
+ (> (length value) rng-memo-data-deriv-max-length)
+ (rng-ipattern-set-memo-map-data-deriv
+ ipattern
+ (rng-memo-map-add (if context (cons value context) value)
+ deriv
+ (rng-ipattern-get-memo-map-data-deriv ipattern)
+ t)))
+ deriv)
+
+(defun rng-compute-data-deriv (ipattern value)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'text) ipattern)
+ ((eq type 'choice)
+ (rng-transform-choice `(lambda (p) (rng-data-deriv p ,value))
+ ipattern))
+ ((eq type 'group)
+ (rng-transform-group-nullable
+ `(lambda (p) (rng-data-deriv p ,value))
+ (lambda (x y) (rng-intern-group (cons x y)))
+ ipattern))
+ ((eq type 'one-or-more)
+ (rng-intern-group (list (rng-data-deriv
+ (rng-ipattern-get-child ipattern)
+ value)
+ (rng-intern-optional ipattern))))
+ ((eq type 'after)
+ (let ((child (rng-ipattern-get-child ipattern)))
+ (if (or (rng-ipattern-get-nullable
+ (rng-data-deriv child value))
+ (and (rng-ipattern-get-nullable child)
+ (rng-blank-p value)))
+ (rng-ipattern-get-after ipattern)
+ rng-not-allowed-ipattern)))
+ ((eq type 'data)
+ (if (rng-dt-make-value (rng-ipattern-get-datatype ipattern)
+ value)
+ rng-empty-ipattern
+ rng-not-allowed-ipattern))
+ ((eq type 'data-except)
+ (if (and (rng-dt-make-value (rng-ipattern-get-datatype ipattern)
+ value)
+ (not (rng-ipattern-get-nullable
+ (rng-data-deriv
+ (rng-ipattern-get-child ipattern)
+ value))))
+ rng-empty-ipattern
+ rng-not-allowed-ipattern))
+ ((eq type 'value)
+ (if (equal (rng-dt-make-value (rng-ipattern-get-datatype ipattern)
+ value)
+ (rng-ipattern-get-value-object ipattern))
+ rng-empty-ipattern
+ rng-not-allowed-ipattern))
+ ((eq type 'list)
+ (let ((tokens (split-string value))
+ (state (rng-ipattern-get-child ipattern)))
+ (while (and tokens
+ (not (eq state rng-not-allowed-ipattern)))
+ (setq state (rng-data-deriv state (car tokens)))
+ (setq tokens (cdr tokens)))
+ (if (rng-ipattern-get-nullable state)
+ rng-empty-ipattern
+ rng-not-allowed-ipattern)))
+ ;; don't think interleave can occur
+ ;; since we do text-only-deriv first
+ (t rng-not-allowed-ipattern))))
+
+(defun rng-transform-multi (f ipattern interner)
+ (let* ((members (rng-ipattern-get-child ipattern))
+ (transformed (mapcar f members)))
+ (if (rng-members-eq members transformed)
+ ipattern
+ (funcall interner transformed))))
+
+(defun rng-transform-choice (f ipattern)
+ (rng-transform-multi f ipattern 'rng-intern-choice))
+
+(defun rng-transform-group (f ipattern)
+ (rng-transform-multi f ipattern 'rng-intern-group))
+
+(defun rng-transform-interleave (f ipattern)
+ (rng-transform-multi f ipattern 'rng-intern-interleave))
+
+(defun rng-transform-one-or-more (f ipattern)
+ (let* ((child (rng-ipattern-get-child ipattern))
+ (transformed (funcall f child)))
+ (if (eq child transformed)
+ ipattern
+ (rng-intern-one-or-more transformed))))
+
+(defun rng-transform-after-child (f ipattern)
+ (let* ((child (rng-ipattern-get-child ipattern))
+ (transformed (funcall f child)))
+ (if (eq child transformed)
+ ipattern
+ (rng-intern-after transformed
+ (rng-ipattern-get-after ipattern)))))
+
+(defun rng-transform-interleave-single (f subster ipattern)
+ (let ((children (rng-ipattern-get-child ipattern))
+ found)
+ (while (and children (not found))
+ (let* ((child (car children))
+ (transformed (funcall f child)))
+ (if (eq transformed rng-not-allowed-ipattern)
+ (setq children (cdr children))
+ (setq found
+ (funcall subster
+ transformed
+ child
+ (rng-ipattern-get-child ipattern))))))
+ (or found
+ rng-not-allowed-ipattern)))
+
+(defun rng-transform-group-nullable (f conser ipattern)
+ "Given a group x1,...,xn,y1,...,yn where the xs are all
+nullable and y1 isn't, return a choice
+ (conser f(x1) x2,...,xm,y1,...,yn)
+ |(conser f(x2) x3,...,xm,y1,...,yn)
+ |...
+ |(conser f(xm) y1,...,yn)
+ |(conser f(y1) y2,...,yn)"
+ (rng-intern-choice
+ (rng-transform-group-nullable-gen-choices
+ f
+ conser
+ (rng-ipattern-get-child ipattern))))
+
+(defun rng-transform-group-nullable-gen-choices (f conser members)
+ (let ((head (car members))
+ (tail (cdr members)))
+ (if tail
+ (cons (funcall conser (funcall f head) tail)
+ (if (rng-ipattern-get-nullable head)
+ (rng-transform-group-nullable-gen-choices f conser tail)
+ nil))
+ (list (funcall f head)))))
+
+(defun rng-members-eq (list1 list2)
+ (while (and list1
+ list2
+ (eq (car list1) (car list2)))
+ (setq list1 (cdr list1))
+ (setq list2 (cdr list2)))
+ (and (null list1) (null list2)))
+
+
+(defun rng-ipattern-after (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'choice)
+ (rng-transform-choice 'rng-ipattern-after ipattern))
+ ((eq type 'after)
+ (rng-ipattern-get-after ipattern))
+ ((eq type 'not-allowed)
+ ipattern)
+ (t (error "Internal error in rng-ipattern-after: unexpected type %s" type)))))
+
+(defun rng-unknown-start-tag-open-deriv (ipattern)
+ (rng-intern-after (rng-compile rng-any-content) ipattern))
+
+(defun rng-ipattern-optionalize-elements (ipattern)
+ (let* ((type (rng-ipattern-get-type ipattern))
+ (transform (assq type rng-transform-map)))
+ (cond (transform
+ (funcall (cdr transform)
+ 'rng-ipattern-optionalize-elements
+ ipattern))
+ ((eq type 'element)
+ (rng-intern-optional ipattern))
+ (t ipattern))))
+
+(defun rng-ipattern-empty-before-p (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'after)
+ (eq (rng-ipattern-get-child ipattern) rng-empty-ipattern))
+ ((eq type 'choice)
+ (let ((members (rng-ipattern-get-child ipattern))
+ (ret t))
+ (while (and members ret)
+ (or (rng-ipattern-empty-before-p (car members))
+ (setq ret nil))
+ (setq members (cdr members)))
+ ret))
+ (t nil))))
+
+(defun rng-ipattern-possible-start-tags (ipattern accum)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'after)
+ (rng-ipattern-possible-start-tags
+ (rng-ipattern-get-child ipattern)
+ accum))
+ ((memq type '(choice interleave))
+ (let ((members (rng-ipattern-get-child ipattern)))
+ (while members
+ (setq accum
+ (rng-ipattern-possible-start-tags (car members)
+ accum))
+ (setq members (cdr members))))
+ accum)
+ ((eq type 'group)
+ (let ((members (rng-ipattern-get-child ipattern)))
+ (while members
+ (setq accum
+ (rng-ipattern-possible-start-tags (car members)
+ accum))
+ (setq members
+ (and (rng-ipattern-get-nullable (car members))
+ (cdr members)))))
+ accum)
+ ((eq type 'element)
+ (if (eq (rng-element-get-child ipattern) rng-not-allowed-ipattern)
+ accum
+ (rng-name-class-possible-names
+ (rng-ipattern-get-name-class ipattern)
+ accum)))
+ ((eq type 'one-or-more)
+ (rng-ipattern-possible-start-tags
+ (rng-ipattern-get-child ipattern)
+ accum))
+ (t accum))))
+
+(defun rng-ipattern-start-tag-possible-p (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((memq type '(after one-or-more))
+ (rng-ipattern-start-tag-possible-p
+ (rng-ipattern-get-child ipattern)))
+ ((memq type '(choice interleave))
+ (let ((members (rng-ipattern-get-child ipattern))
+ (possible nil))
+ (while (and members (not possible))
+ (setq possible
+ (rng-ipattern-start-tag-possible-p (car members)))
+ (setq members (cdr members)))
+ possible))
+ ((eq type 'group)
+ (let ((members (rng-ipattern-get-child ipattern))
+ (possible nil))
+ (while (and members (not possible))
+ (setq possible
+ (rng-ipattern-start-tag-possible-p (car members)))
+ (setq members
+ (and (rng-ipattern-get-nullable (car members))
+ (cdr members))))
+ possible))
+ ((eq type 'element)
+ (not (eq (rng-element-get-child ipattern)
+ rng-not-allowed-ipattern)))
+ (t nil))))
+
+(defun rng-ipattern-possible-attributes (ipattern accum)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'after)
+ (rng-ipattern-possible-attributes (rng-ipattern-get-child ipattern)
+ accum))
+ ((memq type '(choice interleave group))
+ (let ((members (rng-ipattern-get-child ipattern)))
+ (while members
+ (setq accum
+ (rng-ipattern-possible-attributes (car members)
+ accum))
+ (setq members (cdr members))))
+ accum)
+ ((eq type 'attribute)
+ (rng-name-class-possible-names
+ (rng-ipattern-get-name-class ipattern)
+ accum))
+ ((eq type 'one-or-more)
+ (rng-ipattern-possible-attributes
+ (rng-ipattern-get-child ipattern)
+ accum))
+ (t accum))))
+
+(defun rng-ipattern-possible-values (ipattern accum)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'after)
+ (rng-ipattern-possible-values (rng-ipattern-get-child ipattern)
+ accum))
+ ((eq type 'choice)
+ (let ((members (rng-ipattern-get-child ipattern)))
+ (while members
+ (setq accum
+ (rng-ipattern-possible-values (car members)
+ accum))
+ (setq members (cdr members))))
+ accum)
+ ((eq type 'value)
+ (let ((value-object (rng-ipattern-get-value-object ipattern)))
+ (if (stringp value-object)
+ (cons value-object accum)
+ accum)))
+ (t accum))))
+
+(defun rng-ipattern-required-element (ipattern)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((memq type '(after one-or-more))
+ (rng-ipattern-required-element (rng-ipattern-get-child ipattern)))
+ ((eq type 'choice)
+ (let* ((members (rng-ipattern-get-child ipattern))
+ (required (rng-ipattern-required-element (car members))))
+ (while (and required
+ (setq members (cdr members)))
+ (unless (equal required
+ (rng-ipattern-required-element (car members)))
+ (setq required nil)))
+ required))
+ ((eq type 'group)
+ (let ((members (rng-ipattern-get-child ipattern))
+ required)
+ (while (and (not (setq required
+ (rng-ipattern-required-element
+ (car members))))
+ (rng-ipattern-get-nullable (car members))
+ (setq members (cdr members))))
+ required))
+ ((eq type 'interleave)
+ (let ((members (rng-ipattern-get-child ipattern))
+ required)
+ (while members
+ (let ((tem (rng-ipattern-required-element (car members))))
+ (cond ((not tem)
+ (setq members (cdr members)))
+ ((not required)
+ (setq required tem)
+ (setq members (cdr members)))
+ ((equal required tem)
+ (setq members (cdr members)))
+ (t
+ (setq required nil)
+ (setq members nil)))))
+ required))
+ ((eq type 'element)
+ (let ((nc (rng-ipattern-get-name-class ipattern)))
+ (and (consp nc)
+ (not (eq (rng-element-get-child ipattern)
+ rng-not-allowed-ipattern))
+ nc))))))
+
+(defun rng-ipattern-required-attributes (ipattern accum)
+ (let ((type (rng-ipattern-get-type ipattern)))
+ (cond ((eq type 'after)
+ (rng-ipattern-required-attributes (rng-ipattern-get-child ipattern)
+ accum))
+ ((memq type '(interleave group))
+ (let ((members (rng-ipattern-get-child ipattern)))
+ (while members
+ (setq accum
+ (rng-ipattern-required-attributes (car members)
+ accum))
+ (setq members (cdr members))))
+ accum)
+ ((eq type 'choice)
+ (let ((members (rng-ipattern-get-child ipattern))
+ in-all in-this new-in-all)
+ (setq in-all
+ (rng-ipattern-required-attributes (car members)
+ nil))
+ (while (and in-all (setq members (cdr members)))
+ (setq in-this
+ (rng-ipattern-required-attributes (car members) nil))
+ (setq new-in-all nil)
+ (while in-this
+ (when (member (car in-this) in-all)
+ (setq new-in-all
+ (cons (car in-this) new-in-all)))
+ (setq in-this (cdr in-this)))
+ (setq in-all new-in-all))
+ (append in-all accum)))
+ ((eq type 'attribute)
+ (let ((nc (rng-ipattern-get-name-class ipattern)))
+ (if (consp nc)
+ (cons nc accum)
+ accum)))
+ ((eq type 'one-or-more)
+ (rng-ipattern-required-attributes (rng-ipattern-get-child ipattern)
+ accum))
+ (t accum))))
+
+(defun rng-compile-error (&rest args)
+ (signal 'rng-compile-error
+ (list (apply 'format args))))
+
+(put 'rng-compile-error
+ 'error-conditions
+ '(error rng-error rng-compile-error))
+
+(put 'rng-compile-error
+ 'error-message
+ "Incorrect schema")
+
+
+;;; External API
+
+(defsubst rng-match-state () rng-match-state)
+
+(defsubst rng-set-match-state (state)
+ (setq rng-match-state state))
+
+(defsubst rng-match-state-equal (state)
+ (eq state rng-match-state))
+
+(defun rng-schema-changed ()
+ (rng-ipattern-clear)
+ (rng-compile-clear))
+
+(defun rng-match-init-buffer ()
+ (make-local-variable 'rng-compile-table)
+ (make-local-variable 'rng-ipattern-table)
+ (make-local-variable 'rng-last-ipattern-index))
+
+(defun rng-match-start-document ()
+ (rng-ipattern-maybe-init)
+ (rng-compile-maybe-init)
+ (add-hook 'rng-schema-change-hook 'rng-schema-changed nil t)
+ (setq rng-match-state (rng-compile rng-current-schema)))
+
+(defun rng-match-start-tag-open (name)
+ (rng-update-match-state (rng-start-tag-open-deriv rng-match-state
+ name)))
+
+(defun rng-match-attribute-name (name)
+ (rng-update-match-state (rng-start-attribute-deriv rng-match-state
+ name)))
+
+(defun rng-match-attribute-value (value)
+ (rng-update-match-state (rng-data-deriv rng-match-state
+ value)))
+
+(defun rng-match-element-value (value)
+ (and (rng-update-match-state (rng-text-only-deriv rng-match-state))
+ (rng-update-match-state (rng-data-deriv rng-match-state
+ value))))
+
+(defun rng-match-start-tag-close ()
+ (rng-update-match-state (rng-start-tag-close-deriv rng-match-state)))
+
+(defun rng-match-mixed-text ()
+ (rng-update-match-state (rng-mixed-text-deriv rng-match-state)))
+
+(defun rng-match-end-tag ()
+ (rng-update-match-state (rng-end-tag-deriv rng-match-state)))
+
+(defun rng-match-after ()
+ (rng-update-match-state
+ (rng-ipattern-after rng-match-state)))
+
+(defun rng-match-out-of-context-start-tag-open (name)
+ (let* ((found (rng-map-element-attribute 'rng-find-element-content-pattern
+ rng-current-schema
+ nil
+ name))
+ (content-pattern (if found
+ (rng-intern-choice found)
+ rng-not-allowed-ipattern)))
+ (rng-update-match-state
+ (rng-intern-after content-pattern rng-match-state))))
+
+(defun rng-match-possible-namespace-uris ()
+ "Return a list of all the namespace URIs used in the current schema.
+The absent URI is not included, so the result is always list of symbols."
+ (rng-map-element-attribute (lambda (pattern accum)
+ (rng-find-name-class-uris (nth 1 pattern)
+ accum))
+ rng-current-schema
+ nil))
+
+(defun rng-match-unknown-start-tag-open ()
+ (rng-update-match-state
+ (rng-unknown-start-tag-open-deriv rng-match-state)))
+
+(defun rng-match-optionalize-elements ()
+ (rng-update-match-state
+ (rng-ipattern-optionalize-elements rng-match-state)))
+
+(defun rng-match-ignore-attributes ()
+ (rng-update-match-state
+ (rng-ignore-attributes-deriv rng-match-state)))
+
+(defun rng-match-text-typed-p ()
+ (rng-ipattern-text-typed-p rng-match-state))
+
+(defun rng-match-empty-content ()
+ (if (rng-match-text-typed-p)
+ (rng-match-element-value "")
+ (rng-match-end-tag)))
+
+(defun rng-match-empty-before-p ()
+ "Return non-nil if what can be matched before an end-tag is empty.
+In other words, return non-nil if the pattern for what can be matched
+for an end-tag is equivalent to empty."
+ (rng-ipattern-empty-before-p rng-match-state))
+
+(defun rng-match-infer-start-tag-namespace (local-name)
+ (let ((ncs (rng-ipattern-possible-start-tags rng-match-state nil))
+ (nc nil)
+ (ns nil))
+ (while ncs
+ (setq nc (car ncs))
+ (if (and (equal (cdr nc) local-name)
+ (symbolp (car nc)))
+ (cond ((not ns)
+ ;; first possible namespace
+ (setq ns (car nc))
+ (setq ncs (cdr ncs)))
+ ((equal ns (car nc))
+ ;; same as first namespace
+ (setq ncs (cdr ncs)))
+ (t
+ ;; more than one possible namespace
+ (setq ns nil)
+ (setq ncs nil)))
+ (setq ncs (cdr ncs))))
+ ns))
+
+(defun rng-match-nullable-p ()
+ (rng-ipattern-get-nullable rng-match-state))
+
+(defun rng-match-possible-start-tag-names ()
+ "Return a list of possible names that would be valid for start-tags.
+
+Each possible name is returned as a (NAMESPACE . LOCAL-NAME) pair,
+where NAMESPACE is a symbol or nil (meaning the absent namespace) and
+LOCAL-NAME is a string. The returned list may contain duplicates."
+ (rng-ipattern-possible-start-tags rng-match-state nil))
+
+;; This is no longer used. It might be useful so leave it in for now.
+(defun rng-match-start-tag-possible-p ()
+ "Return non-nil if a start-tag is possible."
+ (rng-ipattern-start-tag-possible-p rng-match-state))
+
+(defun rng-match-possible-attribute-names ()
+ "Return a list of possible names that would be valid for attributes.
+
+See the function `rng-match-possible-start-tag-names' for
+more information."
+ (rng-ipattern-possible-attributes rng-match-state nil))
+
+(defun rng-match-possible-value-strings ()
+ "Return a list of strings that would be valid as content.
+The list may contain duplicates. Typically, the list will not
+be exhaustive."
+ (rng-ipattern-possible-values rng-match-state nil))
+
+(defun rng-match-required-element-name ()
+ "Return the name of an element which must occur, or nil if none."
+ (rng-ipattern-required-element rng-match-state))
+
+(defun rng-match-required-attribute-names ()
+ "Return a list of names of attributes which must all occur."
+ (rng-ipattern-required-attributes rng-match-state nil))
+
+(defmacro rng-match-save (&rest body)
+ (let ((state (make-symbol "state")))
+ `(let ((,state rng-match-state))
+ (unwind-protect
+ (progn ,@body)
+ (setq rng-match-state ,state)))))
+
+(put 'rng-match-save 'lisp-indent-function 0)
+(def-edebug-spec rng-match-save t)
+
+(defmacro rng-match-with-schema (schema &rest body)
+ `(let ((rng-current-schema ,schema)
+ rng-match-state
+ rng-compile-table
+ rng-ipattern-table
+ rng-last-ipattern-index)
+ (rng-ipattern-maybe-init)
+ (rng-compile-maybe-init)
+ (setq rng-match-state (rng-compile rng-current-schema))
+ ,@body))
+
+(put 'rng-match-with-schema 'lisp-indent-function 1)
+(def-edebug-spec rng-match-with-schema t)
+
+(provide 'rng-match)
+
+;;; rng-match.el ends here