Mercurial > emacs
comparison lisp/xml.el @ 52975:6958c2be0aa9
Allow comments following the top-level element.
Separate out namespace parsing into special functions.
Change namespace parsing to return ('ns-uri . "local-name")
instead of '{ns-uri}local-name.
author | Eli Zaretskii <eliz@gnu.org> |
---|---|
date | Sat, 01 Nov 2003 17:56:08 +0000 |
parents | 695cf19ef79e |
children | 45bd3ad34572 |
comparison
equal
deleted
inserted
replaced
52974:f45cf0ff5cb3 | 52975:6958c2be0aa9 |
---|---|
206 (goto-char (point-min)) | 206 (goto-char (point-min)) |
207 (while (not (eobp)) | 207 (while (not (eobp)) |
208 (if (search-forward "<" nil t) | 208 (if (search-forward "<" nil t) |
209 (progn | 209 (progn |
210 (forward-char -1) | 210 (forward-char -1) |
211 (if xml | 211 (setq result (xml-parse-tag parse-dtd parse-ns)) |
212 (if (and xml result) | |
212 ;; translation of rule [1] of XML specifications | 213 ;; translation of rule [1] of XML specifications |
213 (error "XML files can have only one toplevel tag") | 214 (error "XML files can have only one toplevel tag") |
214 (setq result (xml-parse-tag parse-dtd parse-ns)) | |
215 (cond | 215 (cond |
216 ((null result)) | 216 ((null result)) |
217 ((listp (car result)) | 217 ((and (listp (car result)) |
218 parse-dtd) | |
218 (setq dtd (car result)) | 219 (setq dtd (car result)) |
219 (if (cdr result) ; possible leading comment | 220 (if (cdr result) ; possible leading comment |
220 (add-to-list 'xml (cdr result)))) | 221 (add-to-list 'xml (cdr result)))) |
221 (t | 222 (t |
222 (add-to-list 'xml result))))) | 223 (add-to-list 'xml result))))) |
223 (goto-char (point-max)))) | 224 (goto-char (point-max)))) |
224 (if parse-dtd | 225 (if parse-dtd |
225 (cons dtd (nreverse xml)) | 226 (cons dtd (nreverse xml)) |
226 (nreverse xml))))))) | 227 (nreverse xml))))))) |
227 | 228 |
229 (defun xml-ns-parse-ns-attrs (attr-list &optional xml-ns) | |
230 "Parse the namespace attributes and return a list of cons in the form: | |
231 \(namespace . prefix)" | |
232 | |
233 (mapcar | |
234 (lambda (attr) | |
235 (let* ((splitup (split-string (car attr) ":")) | |
236 (prefix (nth 0 splitup)) | |
237 (lname (nth 1 splitup))) | |
238 (when (string= "xmlns" prefix) | |
239 (push (cons (if lname | |
240 lname | |
241 "") | |
242 (cdr attr)) | |
243 xml-ns)))) attr-list) | |
244 xml-ns) | |
245 | |
246 ;; expand element names | |
247 (defun xml-ns-expand-el (el xml-ns) | |
248 "Expand the XML elements from \"prefix:local-name\" to a cons in the form | |
249 \"(namespace . local-name)\"." | |
250 | |
251 (let* ((splitup (split-string el ":")) | |
252 (lname (or (nth 1 splitup) | |
253 (nth 0 splitup))) | |
254 (prefix (if (nth 1 splitup) | |
255 (nth 0 splitup) | |
256 (if (string= lname "xmlns") | |
257 "xmlns" | |
258 ""))) | |
259 (ns (cdr (assoc-string prefix xml-ns)))) | |
260 (if (string= "" ns) | |
261 lname | |
262 (cons (intern (concat ":" ns)) | |
263 lname)))) | |
264 | |
265 ;; expand attribute names | |
266 (defun xml-ns-expand-attr (attr-list xml-ns) | |
267 "Expand the attribute list for a particular element from the form | |
268 \"prefix:local-name\" to the form \"{namespace}:local-name\"." | |
269 | |
270 (mapcar | |
271 (lambda (attr) | |
272 (let* ((splitup (split-string (car attr) ":")) | |
273 (lname (or (nth 1 splitup) | |
274 (nth 0 splitup))) | |
275 (prefix (if (nth 1 splitup) | |
276 (nth 0 splitup) | |
277 (if (string= (car attr) "xmlns") | |
278 "xmlns" | |
279 ""))) | |
280 (ns (cdr (assoc-string prefix xml-ns)))) | |
281 (setcar attr | |
282 (if (string= "" ns) | |
283 lname | |
284 (cons (intern (concat ":" ns)) | |
285 lname))))) | |
286 attr-list) | |
287 attr-list) | |
288 | |
289 | |
290 (defun xml-intern-attrlist (attr-list) | |
291 "Convert attribute names to symbols for backward compatibility." | |
292 (mapcar (lambda (attr) | |
293 (setcar attr (intern (car attr)))) | |
294 attr-list) | |
295 attr-list) | |
228 | 296 |
229 (defun xml-parse-tag (&optional parse-dtd parse-ns) | 297 (defun xml-parse-tag (&optional parse-dtd parse-ns) |
230 "Parse the tag at point. | 298 "Parse the tag at point. |
231 If PARSE-DTD is non-nil, the DTD of the document, if any, is parsed and | 299 If PARSE-DTD is non-nil, the DTD of the document, if any, is parsed and |
232 returned as the first element in the list. | 300 returned as the first element in the list. |
274 ((looking-at "</") | 342 ((looking-at "</") |
275 '()) | 343 '()) |
276 ;; opening tag | 344 ;; opening tag |
277 ((looking-at "<\\([^/>[:space:]]+\\)") | 345 ((looking-at "<\\([^/>[:space:]]+\\)") |
278 (goto-char (match-end 1)) | 346 (goto-char (match-end 1)) |
347 | |
348 ;; Parse this node | |
279 (let* ((node-name (match-string 1)) | 349 (let* ((node-name (match-string 1)) |
280 ;; Parse the attribute list. | 350 (attr-list (xml-parse-attlist)) |
281 (children (list (xml-parse-attlist) (intern node-name))) | 351 (children (if (consp xml-ns) ;; take care of namespace parsing |
352 (progn | |
353 (setq xml-ns (xml-ns-parse-ns-attrs | |
354 attr-list xml-ns)) | |
355 (list (xml-ns-expand-attr | |
356 attr-list xml-ns) | |
357 (xml-ns-expand-el | |
358 node-name xml-ns))) | |
359 (list (xml-intern-attrlist attr-list) | |
360 (intern node-name)))) | |
282 pos) | 361 pos) |
283 | |
284 ;; add the xmlns:* attrs to our cache | |
285 (when (consp xml-ns) | |
286 (mapcar | |
287 (lambda (attr) | |
288 (let* ((splitup (split-string (symbol-name (car attr)) ":")) | |
289 (prefix (nth 0 splitup)) | |
290 (lname (nth 1 splitup))) | |
291 (when (string= "xmlns" prefix) | |
292 (setq xml-ns (append (list (cons (if lname | |
293 lname | |
294 "") | |
295 (cdr attr))) | |
296 xml-ns))))) | |
297 (car children)) | |
298 | |
299 ;; expand element names | |
300 (let* ((splitup (split-string (symbol-name (cadr children)) ":")) | |
301 (lname (or (nth 1 splitup) | |
302 (nth 0 splitup))) | |
303 (prefix (if (nth 1 splitup) | |
304 (nth 0 splitup) | |
305 ""))) | |
306 (setcdr children (list | |
307 (intern (concat "{" | |
308 (cdr (assoc-string prefix xml-ns)) | |
309 "}" lname))))) | |
310 | |
311 ;; expand attribute names | |
312 (mapcar | |
313 (lambda (attr) | |
314 (let* ((splitup (split-string (symbol-name (car attr)) ":")) | |
315 (lname (or (nth 1 splitup) | |
316 (nth 0 splitup))) | |
317 (prefix (if (nth 1 splitup) | |
318 (nth 0 splitup) | |
319 (caar xml-ns)))) | |
320 | |
321 (setcar attr (intern (concat "{" | |
322 (cdr (assoc-string prefix xml-ns)) | |
323 "}" lname))))) | |
324 (car children))) | |
325 | 362 |
326 ;; is this an empty element ? | 363 ;; is this an empty element ? |
327 (if (looking-at "/>") | 364 (if (looking-at "/>") |
328 (progn | 365 (progn |
329 (forward-char 2) | 366 (forward-char 2) |
375 (error "XML: Invalid attribute list"))))) | 412 (error "XML: Invalid attribute list"))))) |
376 (t ;; This is not a tag. | 413 (t ;; This is not a tag. |
377 (error "XML: Invalid character"))))) | 414 (error "XML: Invalid character"))))) |
378 | 415 |
379 (defun xml-parse-attlist () | 416 (defun xml-parse-attlist () |
380 "Return the attribute-list after point.Leave point at the first non-blank character after the tag." | 417 "Return the attribute-list after point. Leave point at the |
418 first non-blank character after the tag." | |
381 (let ((attlist ()) | 419 (let ((attlist ()) |
382 start-pos name) | 420 end-pos name) |
383 (skip-syntax-forward " ") | 421 (skip-syntax-forward " ") |
384 (while (looking-at (eval-when-compile | 422 (while (looking-at (eval-when-compile |
385 (concat "\\(" xml-name-regexp "\\)\\s-*=\\s-*"))) | 423 (concat "\\(" xml-name-regexp "\\)\\s-*=\\s-*"))) |
386 (setq name (intern (match-string 1))) | 424 (setq name (match-string 1)) |
387 (goto-char (match-end 0)) | 425 (goto-char (match-end 0)) |
388 | 426 |
389 ;; See also: http://www.w3.org/TR/2000/REC-xml-20001006#AVNormalize | 427 ;; See also: http://www.w3.org/TR/2000/REC-xml-20001006#AVNormalize |
390 | 428 |
391 ;; Do we have a string between quotes (or double-quotes), | 429 ;; Do we have a string between quotes (or double-quotes), |
392 ;; or a simple word ? | 430 ;; or a simple word ? |
393 (if (looking-at "\"\\([^\"]*\\)\"") | 431 (if (looking-at "\"\\([^\"]*\\)\"") |
394 (setq start-pos (match-beginning 0)) | 432 (setq end-pos (match-end 0)) |
395 (if (looking-at "'\\([^']*\\)'") | 433 (if (looking-at "'\\([^']*\\)'") |
396 (setq start-pos (match-beginning 0)) | 434 (setq end-pos (match-end 0)) |
397 (error "XML: Attribute values must be given between quotes"))) | 435 (error "XML: Attribute values must be given between quotes"))) |
398 | 436 |
399 ;; Each attribute must be unique within a given element | 437 ;; Each attribute must be unique within a given element |
400 (if (assoc name attlist) | 438 (if (assoc name attlist) |
401 (error "XML: each attribute must be unique within an element")) | 439 (error "XML: each attribute must be unique within an element")) |
405 (let ((string (match-string 1)) | 443 (let ((string (match-string 1)) |
406 (pos 0)) | 444 (pos 0)) |
407 (replace-regexp-in-string "\\s-\\{2,\\}" " " string) | 445 (replace-regexp-in-string "\\s-\\{2,\\}" " " string) |
408 (push (cons name (xml-substitute-special string)) attlist)) | 446 (push (cons name (xml-substitute-special string)) attlist)) |
409 | 447 |
410 (goto-char start-pos) | 448 (goto-char end-pos) |
411 (forward-sexp) ; we have string syntax | |
412 | |
413 (skip-syntax-forward " ")) | 449 (skip-syntax-forward " ")) |
414 (nreverse attlist))) | 450 (nreverse attlist))) |
415 | 451 |
416 ;;******************************************************************* | 452 ;;******************************************************************* |
417 ;;** | 453 ;;** |
488 | 524 |
489 ;; Translation of rule [45] of XML specifications | 525 ;; Translation of rule [45] of XML specifications |
490 ((looking-at | 526 ((looking-at |
491 "<!ELEMENT\\s-+\\([[:alnum:].%;]+\\)\\s-+\\([^>]+\\)>") | 527 "<!ELEMENT\\s-+\\([[:alnum:].%;]+\\)\\s-+\\([^>]+\\)>") |
492 | 528 |
493 (setq element (intern (match-string 1)) | 529 (setq element (match-string 1) |
494 type (match-string-no-properties 2)) | 530 type (match-string-no-properties 2)) |
495 (setq end-pos (match-end 0)) | 531 (setq end-pos (match-end 0)) |
496 | 532 |
497 ;; Translation of rule [46] of XML specifications | 533 ;; Translation of rule [46] of XML specifications |
498 (cond | 534 (cond |
508 (error "XML: Invalid element type in the DTD"))) | 544 (error "XML: Invalid element type in the DTD"))) |
509 | 545 |
510 ;; rule [45]: the element declaration must be unique | 546 ;; rule [45]: the element declaration must be unique |
511 (if (assoc element dtd) | 547 (if (assoc element dtd) |
512 (error "XML: element declarations must be unique in a DTD (<%s>)" | 548 (error "XML: element declarations must be unique in a DTD (<%s>)" |
513 (symbol-name element))) | 549 element) |
514 | 550 |
515 ;; Store the element in the DTD | 551 ;; Store the element in the DTD |
516 (push (list element type) dtd) | 552 (push (list element type) dtd) |
517 (goto-char end-pos)) | 553 (goto-char end-pos)) |
518 ((looking-at "<!--") | 554 ((looking-at "<!--") |
521 (t | 557 (t |
522 (error "XML: Invalid DTD item"))) | 558 (error "XML: Invalid DTD item"))) |
523 | 559 |
524 ;; Skip the end of the DTD | 560 ;; Skip the end of the DTD |
525 (search-forward ">")))) | 561 (search-forward ">")))) |
526 (nreverse dtd))) | 562 (nreverse dtd)))) |
527 | |
528 | 563 |
529 (defun xml-parse-elem-type (string) | 564 (defun xml-parse-elem-type (string) |
530 "Convert element type STRING into a Lisp structure." | 565 "Convert element type STRING into a Lisp structure." |
531 | 566 |
532 (let (elem modifier) | 567 (let (elem modifier) |