Created
January 30, 2018 21:12
-
-
Save gwatt/f93802518c8e479c4475b851b65cf5e6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(library (html) | |
(export html display-html) | |
(import (rnrs)) | |
(define short-tags (make-enumeration '(br img link meta))) | |
(define (short-tag? t) | |
(enum-set-member? t short-tags)) | |
(define (->string obj) | |
(cond | |
[(string? obj) obj] | |
[(eq? (if #f #f) obj) ""] | |
[else | |
(call-with-string-output-port | |
(lambda (p) | |
(display obj p)))])) | |
(define (alist->hashtable alist) | |
(let ([ht (make-hashtable equal-hash equal?)]) | |
(for-each | |
(lambda (x) | |
(hashtable-set! ht (car x) (cdr x))) | |
alist) | |
ht)) | |
(define html-entity-escapes | |
(alist->hashtable '((#\< . "<") (#\> . ">") (#\& . "&") (#\" . """) (#\' . "'")))) | |
(define attr-string-escapes | |
(alist->hashtable '((#\\ . "\\\\") (#\' . "\\'")))) | |
(define (string-replace str replacements) | |
(define strlen (string-length str)) | |
(define (escape i start) | |
(cond | |
[(= i strlen) (list (substring str start i))] | |
[(hashtable-ref replacements (string-ref str i) #f) => | |
(lambda (rep) | |
(cons* (substring str start i) rep | |
(escape (+ i 1) (+ i 1))))] | |
[else (escape (+ i 1) start)])) | |
(apply string-append (escape 0 0))) | |
(define (attrs->string attrs) | |
(apply string-append | |
(map | |
(lambda (attr) | |
(string-append " " | |
(->string (car attr)) "='" | |
(string-replace (->string (cdr attr)) attr-string-escapes) "'")) | |
attrs))) | |
(define join-strings | |
(case-lambda | |
[(string-list) (join-strings string-list "")] | |
[(string-list sep) | |
(cond | |
[(null? string-list) ""] | |
[(null? (cdr string-list)) (car string-list)] | |
[else (string-append (car string-list) sep (join-strings (cdr string-list) sep))])])) | |
(define node->string | |
(case-lambda | |
[(tag) (node->string tag '())] | |
[(tag attrs . children) | |
(let | |
([attr-string (attrs->string attrs)] | |
[tag-string (symbol->string tag)]) | |
(if (null? children) | |
(if (short-tag? tag) | |
(string-append "<" tag-string attr-string "/>") | |
(string-append "<" tag-string attr-string ">" "</" tag-string ">")) | |
(string-append "<" tag-string attr-string ">" | |
(apply string-append (map html children)) | |
"</" tag-string attr-string ">")))])) | |
(define (html obj) | |
(cond | |
[(pair? obj) (apply node->string obj)] | |
[(string? obj) (string-replace obj html-entity-escapes)] | |
[(char? obj) (hashtable-ref html-entity-escapes obj (string obj))] | |
[else (->string obj)])) | |
(define (display-html title . body) | |
(display "Content-Type: text/html;charset=utf-8\n\n") | |
(display | |
(html | |
`(html () | |
(head () | |
(title () ,title) | |
(meta ((charset . utf-8)))) | |
(body () ,@body))))) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment