(define expect
(lambda (sym expr pred)
(if (not (eq? sym (car expr)))
(error (format "expecting '~a', found '~a' in ~a" sym (car expr) expr)))
(if (< (length expr) 2)
(error (format "~a missing a title or topic identifier in ~a" sym expr)))
(let ((title-or-topic (cadr expr)))
(if (pred title-or-topic)
title-or-topic
(error (format "~a title or topic identifier ~a wrong type in ~a" sym title-or-topic expr))))))
(define (symbol-or-string? s) (or (symbol? s) (string? s)))
(define-interface ^toc-converter (chapter-def section-def section-ref recipe-ref))
(define toc-topic
(lambda (topic-sym)
(format "%~a{\"~a\"}%\n" "TOC" (symbol->string topic-sym))))
(define include-topic
(lambda (topic-sym)
(format "%~a{\"~a\"}%\n" "INCLUDE" (symbol->string topic-sym))))
(define twiki-heading
(lambda (n text)
(string-append "---" (make-string n #\+) " " text "\n")))
(define toc-page-converter
(^toc-converter
(lambda (chapter-title sections)
...) (lambda (section-title recipes)
...) (lambda (section-topic)
...) (lambda (recipe-topic)
...)))
(define index-page-converter
(^toc-converter
(lambda (chapter-title sections)
(cons (twiki-heading 1 chapter-title) sections))
(lambda (section-title recipes)
(cons (twiki-heading 2 section-title) recipes))
include-topic toc-topic))
(define with-interface
(lambda (f)
(lambda (impl)
(impl f))))
(define make-toc-converter
(with-interface (lambda (chapter-def section-def section-ref recipe-ref)
(define recipe->twiki
(lambda (recipe)
(let ((topic (expect 'recipe recipe symbol?)))
(recipe-ref topic))))
(define section->twiki
(lambda (section)
(let ((title-or-topic (expect 'section section symbol-or-string?)))
(cond
((symbol? title-or-topic) (section-ref title-or-topic))
((string? title-or-topic) (section-def title-or-topic
(map recipe->twiki (cddr section))))))))
(lambda (chapter)
(let ((title (expect 'chapter chapter string?)))
(chapter-def title (map section->twiki (cddr chapter))))))))
(define cvt-idx (make-toc-converter index-page-converter))
(let ((twiki-toc
(cvt-idx
#cs'(chapter "String Recipes"
(section StringRecipesIntro)
(section "Recipes"
(recipe StringBasics)
(recipe IsAString))
(section "Other recipes of interest"
(recipe GreasyHashBrowns))
(section StringRecipeReferences)))))
(for-each (lambda (item)
(cond
((string? item) (display item))
((pair? item) (for-each display item))
(else (error (format "invalid entry in toc: ~a" item)))))
twiki-toc))