(define-module (sfx)) (use-modules (system reader)) (use-modules (ice-9 match)) (use-modules (ice-9 pretty-print)) (define (pp . args) (pretty-print args) (car (reverse args))) ;; test macro (define-syntax-rule (test-check test-name expr expected) (when (getenv "CHECK_SFX") (format #t "* ~a: " test-name) (let ((expr* expr) (expected* expected)) (if (equal? expr* expected*) (format #t "PASS :)\n") (begin (format #t "FAILED :(\n") (format #t "** expected: ~a\n" expected*) (format #t "** found: ~a\n" expr*)))))) ;;; ;;; sxml->html ;;; (use-modules (ice-9 rdelim)) (use-modules (sxml simple)) (use-modules (srfi srfi-26)) (use-modules (ice-9 match)) (use-modules (ice-9 format)) (use-modules (ice-9 hash-table)) (use-modules (srfi srfi-1)) (use-modules (web uri)) (use-modules ((sxml xpath) #:renamer (symbol-prefix-proc 'sxml:))) (define %void-elements '(area base br col command embed hr img input keygen link meta param source track wbr)) (define (void-element? tag) "Return #t if TAG is a void element." (pair? (memq tag %void-elements))) (define %escape-chars (alist->hash-table '((#\" . "quot") (#\& . "amp") (#\' . "apos") (#\< . "lt") (#\> . "gt")))) (define (string->escaped-html s port) "Write the HTML escaped form of S to PORT." (define (escape c) (let ((escaped (hash-ref %escape-chars c))) (if escaped (format port "&~a;" escaped) (display c port)))) (string-for-each escape s)) (define (object->escaped-html obj port) "Write the HTML escaped form of OBJ to PORT." (string->escaped-html (call-with-output-string (cut display obj <>)) port)) (define (attribute-value->html value port) "Write the HTML escaped form of VALUE to PORT." (if (string? value) (string->escaped-html value port) (object->escaped-html value port))) (define (attribute->html attr value port) "Write ATTR and VALUE to PORT." (format port "~a=\"" attr) (attribute-value->html value port) (display #\" port)) (define (element->html tag attrs body port) "Write the HTML TAG to PORT, where TAG has the attributes in the list ATTRS and the child nodes in BODY." (format port "<~a" tag) (for-each (match-lambda ((attr value) (display #\space port) (attribute->html attr value port))) attrs) (cond ((and (null? body) (void-element? tag)) (display " />" port)) ((eqv? tag 'script) (display #\> port) (unless (null? body) (display (car body) port)) (display "" port)) (else (begin (display #\> port) (for-each (cut sxml->html <> port) body) (format port "" tag))))) (define (doctype->html doctype port) (format port "" doctype)) (define* (sxml->html tree #:optional (port (current-output-port))) "Write the serialized HTML form of TREE to PORT." (match tree (() *unspecified*) (('doctype type) (doctype->html type port)) (((? symbol? tag) ('@ attrs ...) body ...) (element->html tag attrs body port)) (((? symbol? tag) body ...) (element->html tag '() body port)) ((nodes ...) (for-each (cut sxml->html <> port) nodes)) ((? string? text) (string->escaped-html text port)) ;; Render arbitrary Scheme objects, too. (obj (object->escaped-html obj port)))) ;;; skribe (define (%make-skribe-reader) ;; taken from skribilo ;; Note: we use the `r6rs-symbol-*' and `r6rs-number' token readers since ;; they consider square brackets as delimiters. (make-reader (map standard-token-reader `(whitespace sexp string r6rs-number r6rs-symbol-lower-case r6rs-symbol-upper-case quote-quasiquote-unquote semicolon-comment skribe-exp)) #f ;; use the default fault handler 'reader/record-positions)) (define %skribe (%make-skribe-reader)) (define (skribe port) "Extend skribe to read multiple sexp" (let loop ((sexp (%skribe port)) (out '())) (if (eof-object? sexp) (reverse out) (loop (%skribe port) (cons sexp out))))) (define (%skribe->sxml exp) (pk exp) (match exp ((? string? a) a) ((tag ('@ attrs ...)) `(,tag (@ ,@attrs))) ((tag ('@ attrs) ('quasiquote (body ...))) `(,tag (@ ,attrs) ,@(map %skribe->sxml body))) ((tag ('quasiquote (body ...))) `(,tag ,@(map %skribe->sxml body))) (('quasiquote (body ...)) (map %skribe->sxml body)) (('unquote (tag ('@ attrs)('quasiquote (body ...)))) `(,tag (@ ,attrs) ,@(map %skribe->sxml body))) (('unquote (tag ('quasiquote (body ...)))) `(,tag ,@(map %skribe->sxml body))) ((tag elements ...) `(,tag ,@(map %skribe->sxml elements))))) (define (template body-class body) `((doctype "html") (html (head (meta (@ (charset "utf-8"))) (title "a guile mind book") (link (@ (rel "stylesheet") (href "static/normalize.css"))) (link (@ (rel "stylesheet") (href "static/main.css")))) (body (@ (class ,body-class)) (div (@ (id "header"))) (div (@ (id "wrapper")) (div (@ (id "container")) ,body)) (div (@ (id "footer")) (p (a (@ (href "https://creativecommons.org/licenses/by-nc-sa/4.0/")) "Attribution-NonCommercial-ShareAlike 4.0 International"))))))) (define (sfx input output) (sxml->html (template "index" (map %skribe->sxml (skribe input))) output)) (call-with-output-file "index.html" (lambda (output) (call-with-input-file "a-guile-mind-book.sfx" (lambda (input) (sfx input output)))))