From 310cc3c621b410314c8d4448b72d0d6375dcc279 Mon Sep 17 00:00:00 2001
From: Case Duckworth
Date: Sun, 16 Jul 2023 23:36:51 -0500
Subject: First working version
---
boudin.scm | 286 +++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 174 insertions(+), 112 deletions(-)
(limited to 'boudin.scm')
diff --git a/boudin.scm b/boudin.scm
index 082542b..ac22682 100755
--- a/boudin.scm
+++ b/boudin.scm
@@ -16,6 +16,7 @@ boudin --- a little static site generator
(chicanery)
(atom)
(html-parser)
+ (scss)
(srfi 37)
(srfi 152)
(sxpath))
@@ -97,31 +98,41 @@ boudin --- a little static site generator
(cons (string-append "
" (car pars) "
\n")
acc))))))
+(define (preprocess-text str) #| string -> string
+ Preprocess STR before passing it to `expand-string'.
+ For example, replace all '#' with '##' unless followed by a '{' or '('.
+ |#
+ (set! str (irregex-replace/all
+ '(or (: #\# #\# (look-ahead (or #\{ #\()))
+ (: #\# (look-ahead (~ #\{ #\()))
+ (: #\# eos))
+ str
+ "##"))
+ #; (set! str (irregex-replace/all ; XXX: missing end paren
+ '(: #\@ #\()
+ str
+ "#(sxml->html `("))
+ ;; Return transformed string
+ str)
+
(define (expand-string str) #| string -> string
Expand STR by passing it in a port to CHICKEN's #<# string interpolation.
Yes, this is as cursed as it sounds.
To make it slightly less so, all # are duplicated to escape them, except for
those before ( and {. To escape /those/, double them. |#
- (let* ((escaped (irregex-replace/all
- '(or (: #\# #\# (look-ahead (or #\{ #\()))
- (: #\# (look-ahead (~ #\{ #\()))
- (: #\# eos))
- str
- "##"))
- (delim (let loop ((attempt (number->string (pseudo-random-real))))
+ (let* ((delim (let loop ((attempt (number->string (pseudo-random-real))))
(if (irregex-search attempt str)
(loop (number->string (pseudo-random-real)))
attempt)))
(template (make-concatenated-port
(open-input-string (string-append "#<#" delim "\n"))
- (open-input-string escaped)
+ (open-input-string (preprocess-text str))
(open-input-string (string-append "\n" delim "\n"))))
- (expanded (let ((x (open-output-string)))
- (display (eval (read template)
- (interaction-environment))
- x)
- (get-output-string x))))
+ (expanded (with-output-to-string
+ (lambda ()
+ (display (eval (read template)
+ (interaction-environment)))))))
(irregex-replace/all '(: "#"
(* whitespace))
expanded
@@ -149,7 +160,9 @@ boudin --- a little static site generator
(source-path page-source-path)
(dest-path page-dest-path (setter page-dest-path)))
-(define (%read-port port)
+(define (%read-port port) #| port -> string
+ Read PORT until it hits eof and return the results as a string.
+ |#
(let ((chunk-size 512))
(let loop ((next (read-string chunk-size port))
(blank? #f)
@@ -168,7 +181,10 @@ boudin --- a little static site generator
blank?
(cons next acc)))))))
-(define read-port
+(define read-port #| (optional port) -> string
+ Read PORT completely, returning the results as a string.
+ PORT defaults to `current-input-port'.
+ |#
(case-lambda
(() (%read-port (current-input-port)))
((p) (%read-port p))))
@@ -184,7 +200,7 @@ boudin --- a little static site generator
source
dest
file
- (transform-path file (output-directory)))))
+ (transform-path file (build-directory)))))
(define (extract-meta tree) #| sxml -> alist
Extract metadata from TREE's comments.
@@ -271,85 +287,100 @@ boudin --- a little static site generator
(define page-template
(make-parameter
(lambda (page)
- (sxml->html
- `(html (@ (lang "en"))
- (head (title ,(page-meta-ref page "title" "[untitled]")))
- (body ,(let ((title (page-meta-ref page "title" #f)))
- (if title `(h1 ,title) ""))
- ,@(cdr (page-dest page))))))))
+ `(html (@ (lang "en"))
+ (head (title ,(page-meta-ref page "title" "[untitled]"))
+ (meta (@ (name "viewport")
+ (content "initial-scale=1.0"))))
+ (body ,(let ((title (page-meta-ref page "title" #f)))
+ (if title `(h1 ,title) ""))
+ ,@(cdr (page-dest page)))))))
(define index-template
(make-parameter
(lambda pages
- (sxml->html
- `(html (@ (lang "en"))
- (head (title ,(site-name)))
- (body (h1 ,(site-name))
- (ul
- ,@(map (lambda (pg)
- `(li (a (@ (href ,(page-url pg)))
- ,(page-meta-ref pg
- "title"
- (pathname-file
- (page-source-path pg))))))
- pages))))))))
+ `(html (@ (lang "en"))
+ (head (title ,(site-name)))
+ (body (h1 ,(site-name))
+ (ul
+ ,@(map (lambda (pg)
+ `(li (a (@ (href ,(page-url pg)))
+ ,(page-meta-ref pg
+ "title"
+ (pathname-file
+ (page-source-path pg))))))
+ pages)))))))
(define feed-template
(make-parameter
(lambda pages
- (with-output-to-string
- (lambda ()
- (write-atom-doc
- (make-atom-doc
- (make-feed
- title: (make-title (site-name))
- id: (site-base-url)
- updated: (publish-time) ; I don't like these semantics ..
- authors: (list (make-author name: (site-author)
- uri: (site-base-url)))
- links: (list (make-link type: 'html
- uri-language: "en"
- uri: (site-base-url))
- (make-link relation: "self"
- type: "application/atom+xml"
- uri: (make-pathname
- (site-base-url) "feed" "xml")))
- rights: (make-rights (site-rights))
- generator: (make-generator "Boudin"
- uri: "https://git.acdw.net/boudin"
- version: "0.1.0")
- entries: (map (lambda (pg)
- (make-entry
- title: (make-title
- (page-meta-ref pg "title" "[untitled]"))
- links: (list (make-link type: 'html
- uri: (page-url pg)))
- id: (page-url pg)
- updated: (page-guess-updated pg)
- ;;published:
- content:
- `(atom:content (@ (type "html"))
- ,(cdr (page-dest pg)))))
- pages)))))))))
-
-;;; Collecting pages from a directory
-
-(define (collect-pages dir ext)
- (map file->page
- (glob (make-pathname dir "*" ext))))
+ (make-atom-doc
+ (make-feed
+ title: (make-title (site-name))
+ id: (site-base-url)
+ updated: (publish-time) ; I don't like these semantics ..
+ authors: (list (make-author name: (site-author)
+ uri: (site-base-url)))
+ links: (list (make-link type: 'html
+ uri-language: "en"
+ uri: (site-base-url))
+ (make-link relation: "self"
+ type: "application/atom+xml"
+ uri: (make-pathname
+ (site-base-url) "feed" "xml")))
+ rights: (make-rights (site-rights))
+ generator: (make-generator "Boudin"
+ uri: "https://git.acdw.net/boudin"
+ version: "0.1.0")
+ entries: (map (lambda (pg)
+ (make-entry
+ title: (make-title
+ (page-meta-ref pg "title" "[untitled]"))
+ links: (list (make-link type: 'html
+ uri: (page-url pg)))
+ id: (page-url pg)
+ updated: (page-guess-updated pg)
+ ;;published:
+ content:
+ `(atom:content (@ (type "html"))
+ ,(cdr (page-dest pg)))))
+ pages))))))
;;; Publishing
-(define (apply-template template pages)
- (apply template (if (list? pages)
- pages
- (list pages))))
+(define (write-style)
+ (print-log "writing style")
+ (with-output-to-file (make-pathname (build-directory) "style" "css")
+ (lambda () (write-css (site-style)))))
+
+(define (write-page page)
+ (print-log "writing " (or (page-meta-ref page "title" #f)
+ (page-source-path page)))
+ (create-directory (pathname-directory (page-dest-path page)) 'parents)
+ (with-output-to-file (page-dest-path page)
+ (lambda () (sxml-display-as-html ((page-template) page)))))
+
+(define (write-index pages)
+ (print-log "writing index")
+ (with-output-to-file (make-pathname (build-directory) "index" "html")
+ (lambda () (sxml-display-as-html (apply (index-template) pages)))))
+
+(define (write-feed pages)
+ (print-log "writing feed")
+ (with-output-to-file (make-pathname (build-directory) "feed" "xml")
+ (lambda () (write-atom-doc (apply (feed-template) pages)))))
;;; Configuration
-(define output-directory
+;; Build configuration
+
+(define build-directory
(make-parameter "out/"))
+(define build-config
+ (make-parameter "config.scm"))
+
+;; Site configuration
+
(define site-name
(make-parameter "[A boudin web site]"))
@@ -362,58 +393,89 @@ boudin --- a little static site generator
(define site-rights
(make-parameter (string-append "(C) " (site-author))))
+(define site-style
+ (make-parameter
+ `(css+ (body (font "20px/1.4 sans-serif")
+ (max-width "48em")
+ (padding "1em")
+ (margin auto)))))
+
;;; Options & Operands (SRFI 37)
-(define (die error-code message . args)
+(define (print-log . xs)
(with-output-to-port (current-error-port)
- (apply print message args)
- (exit error-code)))
+ (lambda () (apply print xs))))
+
+(define (die error-code . xs)
+ (apply print-log xs)
+ (exit error-code))
-(define usage #<page name) pages))
+ (die 2 "Page not found: " name))
+ seeds)
+ ;; Seeds
+ '())
+ pages))
;;; Main entry point
+(define pages (make-parameter #f))
+
(define (main args)
- (process-args args)
- ;; TODO ...
- #f)
+ (parameterize ((pages (process-args args)))
+ (unless (pages)
+ (die 0 "No pages to process. Abort."))
+ ;; Check for files, create directories
+ (if (file-exists? (build-config))
+ (load (build-config))
+ (print-log "No config.scm found; using default config"))
+ (create-directory (build-directory) 'parents)
+ ;; Build the site
+ (write-style) ; TODO: copy static assets (?)
+ (for-each write-page (pages))
+ (write-index (pages))
+ (write-feed (pages))))
(cond-expand
((or chicken-script compiling)
--
cgit 1.4.1-21-gabe81