;;; ---- Manipulation of eXtended Xml EXPRessions. ;;; Copyright (C) 2004 by Tony Garnock-Jones. ;;; This is free software; you can redistribute it and/or ;;; modify it under the terms of the GNU Lesser General Public ;;; License as published by the Free Software Foundation; either ;;; version 2.1 of the License, or (at your option) any later version. ;;; This software 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 ;;; Lesser General Public License for more details. ;;; You should have received a copy of the GNU Lesser General Public ;;; License along with this software; if not, write to the Free Software ;;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ;;; Author: Tony Garnock-Jones ;; Requires: SRFI-1, SRFI-6, SRFI-13, SRFI-23, SRFI-39 ;; ;; Exports: ;; xml-empty-tags-mode ;; xml-double-quotes-mode ;; xxexpr->string ;; xxexpr->string/notags ;; write-xxexpr ;; pretty-print-xxexpr ;; write-xxexpr/notags) ;; Simplified grammar (omits entities ("special") etc.) for XXEXPRs ;; ;; XXEXPR :== node ;; node :== (child . node) | () ;; child :== edge | atom | special ;; edge :== (tag . node) | (tag ((attr atom) ...) . node) ;; | (tag (@ (attr atom) ...) . node) ;; atom :== ;; ;; Note in particular that write-xxexpr and friends take a /node/ ;; as their argument, not an /edge/! (define xml-empty-tags-mode (make-parameter #t)) (define xml-double-quotes-mode (make-parameter #f)) (define (xxexpr-external-representation datum) (cond ((string? datum) datum) ((char? datum) (string datum)) ((symbol? datum) (symbol->string datum)) ((number? datum) (number->string datum)) (else (let ((o (open-output-string))) (display datum o) (get-output-string o))))) (define make-show-node (let ((make-escaper (lambda (alist) (lambda (orig) (reverse! (string-fold (lambda (ch acc) (cond ((assv ch alist) => (lambda (p) (cons (cdr p) acc))) (else (cons ch acc)))) '() orig)))))) (define xml-escaper (make-escaper '((#\< . "<") (#\> . ">") (#\& . "&")))) (define xml-attribute-escaper (make-escaper '((#\" . """) (#\' . "'")))) (define (show-attrs alist) (map (lambda (p) (list " " (car p) (if (xml-double-quotes-mode) "=\"" "='") (map (lambda (v) (xml-attribute-escaper (xxexpr-external-representation v))) (cdr p)) (if (xml-double-quotes-mode) "\"" "'"))) alist)) (define (show-edge show-node tag attrs body) (if (and (xml-empty-tags-mode) (null? body)) (vector 'open-close (list "<" tag (show-attrs attrs) "/>")) (list (vector 'open (list "<" tag (show-attrs attrs) ">")) (map show-node body) (vector 'close (list ""))))) (define (show-edge/notags show-node tag attrs body) (map show-node body)) (define (show-pi tag attrs) (vector 'open-close (list ""))) (define (show-external-id x) (case (car x) ((public) (list "PUBLIC \"" (cadr x) "\" \"" (caddr x) "\"")) ((system) (list "SYSTEM \"" (cadr x) "\"")) (else (error "Unknown external-id kind" x)))) (define (show-PEDef def) (if (string? def) def (show-external-id def))) (define (show-entity-def body) (vector 'open-close (if (eq? (car body) '%) (list "") (list "")))) (define (show-internal-dtd body0) (list " [" (map (lambda (x) (let ((tag (car x)) (body (cdr x))) (case tag ((*entity*) (show-entity-def body)) ((*literal*) body) (else (error "Unsupported internal-dtd clause" x))))) body0) "]>")) (define (show-doctype basetag decltype body) (vector 'open-close (list "string* s pretty) (string-concatenate (reverse! (let walk ((acc '()) (s s)) (cond ((null? s) acc) ((pair? s) (walk (walk acc (car s)) (cdr s))) ((vector? s) (walk acc (vector-ref s 1))) ;; ignore pretty flag for now (else (cons (xxexpr-external-representation s) acc))))))) (define (xxexpr->string x) (xxexpr->string* (map (make-show-node #f) x) #f)) (define (xxexpr->string/notags x) (xxexpr->string* (map (make-show-node #t) x) #f)) (define write-xxexpr* (let () (define (walk-show pretty p v) (let ((last-was-tag #f) (at-beginning #t) (indent 0) (*delta* 4)) (define (newline-and-indent) (if at-beginning (set! at-beginning #f) (if pretty (begin (p #\newline) (p (make-string indent #\space)))))) (define (bump-indent! up) (set! indent ((if up + -) indent *delta*))) (let walk ((v v)) (cond ((null? v)) ((pair? v) (walk (car v)) (walk (cdr v))) ((vector? v) (case (vector-ref v 0) ((open) (newline-and-indent) (bump-indent! #t)) ((open-close) (newline-and-indent) (set! last-was-tag #t)) ((close) (bump-indent! #f) (if last-was-tag (newline-and-indent))) (else (error "Unknown pretty-printing directive in xxexpr" (vector-ref v 0)))) (walk (vector-ref v 1)) (set! last-was-tag #t)) (else (set! last-was-tag #f) (p v)))))) (lambda (pretty show-result port) (if port (walk-show pretty (lambda (v) (display v port)) show-result) (walk-show pretty display show-result))))) (define (write-xxexpr x . port) (write-xxexpr* #f (map (make-show-node #f) x) (and (pair? port) (car port)))) (define (pretty-print-xxexpr x . port) (write-xxexpr* #t (map (make-show-node #f) x) (and (pair? port) (car port)))) (define (write-xxexpr/notags x . port) (write-xxexpr* #f (map (make-show-node #t) x) (and (pair? port) (car port))))