mirror of
https://github.com/ashinn/chibi-scheme.git
synced 2025-05-18 21:29:19 +02:00
196 lines
7.1 KiB
Scheme
196 lines
7.1 KiB
Scheme
|
|
(define (call-with-output-string proc)
|
|
(let ((out (open-output-string)))
|
|
(proc out)
|
|
(get-output-string out)))
|
|
|
|
(define (display-to-string x)
|
|
(cond ((string? x) x)
|
|
((char? x) (string x))
|
|
((symbol? x) (symbol->string x))
|
|
((number? x) (number->string x))
|
|
(else (error "don't know how to display as sxml" x))))
|
|
|
|
(define (html-display-escaped-attr str . o)
|
|
(let ((start 0)
|
|
(end (string-length str))
|
|
(out (if (pair? o) (car o) (current-output-port))))
|
|
(let lp ((from start) (to start))
|
|
(if (>= to end)
|
|
(display (substring str from to) out)
|
|
(let ((c (string-ref str to)))
|
|
(cond
|
|
((eq? c #\<)
|
|
(display (substring str from to) out)
|
|
(display "<" out)
|
|
(lp (+ to 1) (+ to 1)))
|
|
((eq? c #\&)
|
|
(display (substring str from to) out)
|
|
(display "&" out)
|
|
(lp (+ to 1) (+ to 1)))
|
|
((eq? c #\")
|
|
(display (substring str from to) out)
|
|
(display """ out)
|
|
(lp (+ to 1) (+ to 1)))
|
|
(else
|
|
(lp from (+ to 1)))))))))
|
|
|
|
(define (html-escape-attr str)
|
|
(call-with-output-string
|
|
(lambda (out) (html-display-escaped-attr (display-to-string str) out))))
|
|
|
|
(define (html-attr->string attr)
|
|
(if (null? (cdr attr))
|
|
(symbol->string (car attr))
|
|
(let ((val (if (pair? (cdr attr)) (cadr attr) (cdr attr))))
|
|
(string-append (symbol->string (car attr))
|
|
"=\"" (html-escape-attr val) "\""))))
|
|
|
|
(define (html-tag->string tag attrs)
|
|
(let lp ((ls attrs) (res (list (symbol->string tag) "<")))
|
|
(if (null? ls)
|
|
(apply string-append (reverse (cons ">" res)))
|
|
(lp (cdr ls) (cons (html-attr->string (car ls)) (cons " " res))))))
|
|
|
|
(define void-elements
|
|
'(area base br col embed hr img input keygen link meta param source track wbr))
|
|
|
|
(define (html-display-escaped-string x . o)
|
|
(let* ((str (display-to-string x))
|
|
(start 0)
|
|
(end (string-length str))
|
|
(out (if (pair? o) (car o) (current-output-port))))
|
|
(let lp ((from start) (to start))
|
|
(if (>= to end)
|
|
(display (substring str from to) out)
|
|
(let ((c (string-ref str to)))
|
|
(cond
|
|
((eq? c #\<)
|
|
(display (substring str from to) out)
|
|
(display "<" out)
|
|
(lp (+ to 1) (+ to 1)))
|
|
((eq? c #\&)
|
|
(display (substring str from to) out)
|
|
(display "&" out)
|
|
(lp (+ to 1) (+ to 1)))
|
|
(else
|
|
(lp from (+ to 1)))))))))
|
|
|
|
(define (html-escape str)
|
|
(call-with-output-string
|
|
(lambda (out) (html-display-escaped-string str out))))
|
|
|
|
(define indentable-elements
|
|
'(address article aside base blockquote body dd details dialog
|
|
div dl dt fieldset figcaption figure footer form h1 h2 h3 h4
|
|
h5 h6 head header hgroup hr li link main meta nav ol p pre
|
|
script section style table title ul))
|
|
|
|
(define (indent i out)
|
|
(do ((j (* 2 i) (- j 1))) ((= j 0)) (write-char #\space out)))
|
|
|
|
;;> Render (valid, expanded) \var{sxml} as html.
|
|
;;> \var{@raw} tag is considered safe text and not processed or escaped.
|
|
(define (sxml-display-as-html sxml . args)
|
|
(let* ((out (if (null? args) (current-output-port) (car args)))
|
|
(args (if (null? args) args (cdr args)))
|
|
(indent? (if (null? args) #f (car args)))
|
|
(args (if (null? args) args (cdr args))))
|
|
(unless (null? args) (error "too many args"))
|
|
(let lp ((sxml (if (and (pair? sxml) (eq? '*TOP* (car sxml)))
|
|
(cdr sxml)
|
|
sxml))
|
|
(depth 0))
|
|
(cond
|
|
((pair? sxml)
|
|
(let ((tag (car sxml))
|
|
(rest (cdr sxml)))
|
|
(cond
|
|
((symbol? tag)
|
|
(cond
|
|
((eqv? #\! (string-ref (symbol->string tag) 0))
|
|
(display "<" out) (display tag out)
|
|
(for-each (lambda (x) (display " " out) (display x out)) rest)
|
|
(display ">\n" out))
|
|
((and (eq? '@raw tag)
|
|
(string? (car rest)))
|
|
(if (not (null? (cdr rest)))
|
|
(error "@raw takes only one value" sxml))
|
|
(display (car rest) out))
|
|
((and (pair? rest)
|
|
(pair? (car rest))
|
|
(eq? '@ (caar rest)))
|
|
(when (and indent? (memq tag indentable-elements))
|
|
(newline out)
|
|
(indent depth out))
|
|
(display (html-tag->string tag (cdar rest)) out)
|
|
(for-each (lambda (x) (lp x (+ 1 depth))) (cdr rest))
|
|
(unless (and (null? (cdr rest)) (memq tag void-elements))
|
|
(display "</" out) (display tag out) (display ">" out)))
|
|
(else
|
|
(when (and indent? (memq tag indentable-elements))
|
|
(newline out)
|
|
(indent depth out))
|
|
(display (html-tag->string tag '()) out)
|
|
(for-each (lambda (x) (lp x (+ 1 depth))) rest)
|
|
(unless (and (null? rest) (memq tag void-elements))
|
|
(display "</" out) (display tag out) (display ">" out)))))
|
|
(else
|
|
(for-each (lambda (x) (lp x (+ 1 depth))) sxml)))))
|
|
((null? sxml))
|
|
(else (html-display-escaped-string sxml out))))))
|
|
|
|
;;> Render \var{sxml} as \var{xml}.
|
|
;;> \var{@raw} tag is considered safe text and not processed or escaped.
|
|
(define (sxml->xml sxml)
|
|
(call-with-output-string
|
|
(lambda (out) (sxml-display-as-html sxml out))))
|
|
|
|
;;> Render \var{sxml} as simple text, stripping all tags.
|
|
(define (sxml-strip sxml)
|
|
(call-with-output-string
|
|
(lambda (out)
|
|
(let strip ((x sxml))
|
|
(cond
|
|
((pair? x)
|
|
(for-each
|
|
strip
|
|
(if (and (pair? (cdr x)) (eq? '@ (cadr x))) (cddr x) (cdr x))))
|
|
((string? x)
|
|
(display x out)))))))
|
|
|
|
;;> Render \var{sxml} as text for viewing in a terminal.
|
|
(define (sxml-display-as-text sxml . o)
|
|
(let ((out (if (pair? o) (car o) (current-output-port)))
|
|
(sxml (if (and (pair? sxml) (null? (cddr sxml)) (eq? '*TOP* (car sxml)))
|
|
(cadr sxml)
|
|
sxml)))
|
|
(let lp ((sxml sxml))
|
|
(cond
|
|
((proper-list? sxml)
|
|
(let ((tag (car sxml)))
|
|
(cond
|
|
;; skip headers and the menu
|
|
((or (memq tag '(head style script !DOCTYPE))
|
|
(and (eq? 'div tag)
|
|
(pair? (cdr sxml))
|
|
(pair? (cadr sxml))
|
|
(eq? '@ (car (cadr sxml)))
|
|
(equal? '(id . "menu") (assq 'id (cdr (cadr sxml))))))
|
|
)
|
|
;; recurse other tags, appending newlines for new sections
|
|
((symbol? tag)
|
|
(if (memq tag '(h1 h2 h3 h4 h5 h6))
|
|
(newline out))
|
|
(let ((ls (if (and (pair? (cdr sxml))
|
|
(pair? (cadr sxml))
|
|
(eq? '@ (car (cadr sxml))))
|
|
(cddr sxml)
|
|
(cdr sxml))))
|
|
(for-each lp ls))
|
|
(if (memq tag '(p li br h1 h2 h3 h4 h5 h6))
|
|
(newline out)))
|
|
(else
|
|
(for-each lp sxml)))))
|
|
((null? sxml))
|
|
(else (display sxml out))))))
|