diff options
author | Case Duckworth | 2023-08-15 23:33:17 -0500 |
---|---|---|
committer | Case Duckworth | 2023-08-15 23:33:17 -0500 |
commit | d4830cdd422258a7c91a5ed07af50f8c208a29ee (patch) | |
tree | bacdf4124ef9b9467ea64c6d098a5cd78426912a /lib | |
parent | Etc (diff) | |
download | boudin-d4830cdd422258a7c91a5ed07af50f8c208a29ee.tar.gz boudin-d4830cdd422258a7c91a5ed07af50f8c208a29ee.zip |
A new start (again)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/config.sld | 45 | ||||
-rw-r--r-- | lib/schmaltz.sld | 17 | ||||
-rw-r--r-- | lib/schmaltz.sls | 103 | ||||
-rw-r--r-- | lib/types.sld | 24 | ||||
-rw-r--r-- | lib/types.sls | 72 | ||||
-rw-r--r-- | lib/util.sld | 50 |
6 files changed, 311 insertions, 0 deletions
diff --git a/lib/config.sld b/lib/config.sld new file mode 100644 index 0000000..bdd6ef5 --- /dev/null +++ b/lib/config.sld | |||
@@ -0,0 +1,45 @@ | |||
1 | ;;; (boudin config) --- default values for configuration options | ||
2 | ;; | ||
3 | ;; To change these, make a `config.scm' in your site's root directory and change | ||
4 | ;; these. They're all parameters so .. change em like that. | ||
5 | |||
6 | (define-library (boudin config) | ||
7 | (import (scheme base) | ||
8 | (boudin schmaltz) | ||
9 | ;; not portable | ||
10 | (chicken pathname) | ||
11 | (html-parser) | ||
12 | ) | ||
13 | |||
14 | (export site-url site-dest | ||
15 | page-path-transformers page-text-transformers | ||
16 | page-template index-template feed-template) | ||
17 | |||
18 | (begin | ||
19 | ;; Site information | ||
20 | (define site-url | ||
21 | (make-parameter "example.com")) | ||
22 | (define site-dest | ||
23 | (make-parameter "out/")) | ||
24 | ;; Transformers | ||
25 | (define page-path-transformers | ||
26 | (make-parameter | ||
27 | (list (lambda (path) (make-pathname (site-dest) path))))) | ||
28 | (define page-text-transformers | ||
29 | (make-parameter | ||
30 | (list wrap-paragraphs | ||
31 | render-string | ||
32 | html->sxml))) | ||
33 | ;; Templates --- note that we use quote but include unquote forms here. | ||
34 | ;; This is to simplify the configuration and to avoid a cyclical dependency | ||
35 | ;; with (boudin types). | ||
36 | (define page-template | ||
37 | (make-parameter | ||
38 | '(html (@ (lang "en-us")) | ||
39 | (head (title (or (page-ref pg "title") "[untitled]"))) | ||
40 | (body ,@(page-sxml pg))))) | ||
41 | (define index-template | ||
42 | (make-parameter 'todo)) | ||
43 | (define feed-template | ||
44 | (make-parameter 'todo)) | ||
45 | )) | ||
diff --git a/lib/schmaltz.sld b/lib/schmaltz.sld new file mode 100644 index 0000000..d54d53e --- /dev/null +++ b/lib/schmaltz.sld | |||
@@ -0,0 +1,17 @@ | |||
1 | (define-library (boudin schmaltz) | ||
2 | (export render | ||
3 | render-string | ||
4 | wrap-paragraphs) | ||
5 | |||
6 | (import (scheme base) | ||
7 | (scheme case-lambda) ; case-lambda | ||
8 | (scheme eval) ; eval | ||
9 | (scheme read) ; read | ||
10 | (scheme repl) ; interaction-environment | ||
11 | (scheme write) ; display | ||
12 | (only (html-parser) | ||
13 | sxml->html) | ||
14 | (only (srfi 152) | ||
15 | string-split string-trim)) | ||
16 | |||
17 | (include "lib/schmaltz.sls")) | ||
diff --git a/lib/schmaltz.sls b/lib/schmaltz.sls new file mode 100644 index 0000000..623191f --- /dev/null +++ b/lib/schmaltz.sls | |||
@@ -0,0 +1,103 @@ | |||
1 | ;;; (boudin schmaltz) --- transform almost-html plus scheme into html | ||
2 | |||
3 | ;;; Embedded scheme code | ||
4 | |||
5 | (define (->string x) | ||
6 | (call-with-port (open-output-string) | ||
7 | (lambda (port) | ||
8 | (display x port) | ||
9 | (get-output-string port)))) | ||
10 | |||
11 | (define render-string | ||
12 | (case-lambda | ||
13 | ((s) (render-string s (interaction-environment))) | ||
14 | ((s env) | ||
15 | (call-with-port (open-input-string s) | ||
16 | (lambda (port) | ||
17 | (render port env)))))) | ||
18 | |||
19 | (define (render port env) | ||
20 | ;; A few rough edges: | ||
21 | ;; #, x will try to render x | ||
22 | (define (burn-char) | ||
23 | ;; Throw a character away. I've defined this for clarity below. | ||
24 | (read-char port)) | ||
25 | |||
26 | (let loop ((ch (read-char port)) | ||
27 | (acc '())) | ||
28 | (define (proceed) (loop (read-char port) (cons ch acc))) | ||
29 | (cond | ||
30 | ((not ch) | ||
31 | (loop (read-char port) acc)) | ||
32 | ((eof-object? ch) | ||
33 | (list->string (reverse acc))) | ||
34 | ((eq? ch #\#) ; special processing to come | ||
35 | (case (peek-char port) | ||
36 | ((#\\) ; inhibit processing of the next char | ||
37 | (burn-char) | ||
38 | (loop (read-char port) (cons ch acc))) | ||
39 | ((#\,) ; scheme eval expansion | ||
40 | (burn-char) | ||
41 | (loop #f | ||
42 | (append (let ((s (->string | ||
43 | (eval (read port) | ||
44 | env)))) | ||
45 | (cond | ||
46 | ((equal? s "#<unspecified>") ; XXX NOT PORTABLE | ||
47 | '()) | ||
48 | ((equal? s "#!eof") ; XXX NOT PORTABLE | ||
49 | '(#\, #\#)) | ||
50 | (else (reverse (string->list s))))) | ||
51 | acc))) | ||
52 | ((#\@) ; embedded sxml | ||
53 | (burn-char) | ||
54 | (loop #f | ||
55 | (append (let ((h (eval `(sxml->html ,(list 'quasiquote | ||
56 | (read port))) | ||
57 | env))) | ||
58 | (cond | ||
59 | ((equal? h "#!eof") ; XXX NOT PORTABLE | ||
60 | '(#\@ #\#)) | ||
61 | (else (reverse (string->list h))))) | ||
62 | acc))) | ||
63 | (else (proceed)))) | ||
64 | (else (proceed))))) | ||
65 | |||
66 | ;;; Wrap paragraphs | ||
67 | |||
68 | (define (split-paragraphs str) | ||
69 | (let loop ((lines (string-split str "\n")) | ||
70 | (par '()) | ||
71 | (acc '())) | ||
72 | (cond | ||
73 | ((and (null? lines) ; base case: no more lines | ||
74 | (null? par)) ; ... or pending paragraph | ||
75 | (reverse acc)) | ||
76 | ((null? lines) ; add the final paragraph | ||
77 | (loop '() '() (cons (apply string-append (reverse par)) acc))) | ||
78 | ((equal? (car lines) "") ; paragraph break | ||
79 | (loop (cdr lines) | ||
80 | '() | ||
81 | (cons (apply string-append (reverse par)) acc))) | ||
82 | (else ; line break | ||
83 | (loop (cdr lines) | ||
84 | (cons (string-append (car lines) "\n") par) | ||
85 | acc))))) | ||
86 | |||
87 | (define (wrap-paragraphs str) | ||
88 | (let loop ((pars (split-paragraphs str)) | ||
89 | (acc '())) | ||
90 | (cond | ||
91 | ((null? pars) | ||
92 | (apply string-append (reverse acc))) | ||
93 | ((zero? (string-length (car pars))) | ||
94 | (loop (cdr pars) | ||
95 | acc)) | ||
96 | ((eq? #\< (string-ref (string-trim (car pars)) 0)) | ||
97 | (loop (cdr pars) | ||
98 | (cons (car pars) | ||
99 | acc))) | ||
100 | (else | ||
101 | (loop (cdr pars) | ||
102 | (cons (string-append "<p>" (car pars) "</p>\n") | ||
103 | acc)))))) | ||
diff --git a/lib/types.sld b/lib/types.sld new file mode 100644 index 0000000..791ff53 --- /dev/null +++ b/lib/types.sld | |||
@@ -0,0 +1,24 @@ | |||
1 | (define-library (boudin types) | ||
2 | (import (scheme base) | ||
3 | (scheme case-lambda) | ||
4 | (scheme file) | ||
5 | (boudin config) | ||
6 | (boudin util) | ||
7 | ;; non-portable bits | ||
8 | (chicken pathname) | ||
9 | (html-parser) | ||
10 | ) | ||
11 | |||
12 | (export | ||
13 | ;; pages | ||
14 | make-page page? | ||
15 | page-path page-dest page-text page-sxml page-meta | ||
16 | set-page-dest! set-page-text! set-page-sxml! set-page-meta! | ||
17 | extract-metadata page-ref page-set! | ||
18 | page-url page-slug | ||
19 | read-page write-page | ||
20 | ;; indeces | ||
21 | ;; static files | ||
22 | ) | ||
23 | |||
24 | (include "lib/types.sls")) | ||
diff --git a/lib/types.sls b/lib/types.sls new file mode 100644 index 0000000..2b4da5d --- /dev/null +++ b/lib/types.sls | |||
@@ -0,0 +1,72 @@ | |||
1 | ;;; (boudin types) --- pages, indeces, and static files | ||
2 | |||
3 | ;; All paths are relative to the site directory unless otherwise noted | ||
4 | |||
5 | (define-record-type page | ||
6 | (make-page path ; Input path | ||
7 | dest ; Output path (rel. to output directory) | ||
8 | text ; Input text | ||
9 | sxml ; Rendered sxml | ||
10 | meta ; Metadata (title, etc.) | ||
11 | ) | ||
12 | page? | ||
13 | (path page-path) | ||
14 | (dest page-dest set-page-dest!) | ||
15 | (text page-text set-page-text!) | ||
16 | (sxml page-sxml set-page-sxml!) | ||
17 | (meta page-meta set-page-meta!)) | ||
18 | |||
19 | (define (page-ref pg key) | ||
20 | (assoc-ref key (page-meta pg) (identity #f))) | ||
21 | |||
22 | (define (page-set! pg key val) | ||
23 | (set-page-meta! pg (cons (cons key val) | ||
24 | (page-meta pg)))) | ||
25 | |||
26 | (define (extract-metadata sxml) | ||
27 | #f) | ||
28 | |||
29 | (define (*urlify path) | ||
30 | (normalize-pathname | ||
31 | (make-pathname (list (site-url) | ||
32 | (pathname-strip-extension path)) | ||
33 | "index.html"))) | ||
34 | |||
35 | (define (page-url pg) ; foo.html => http://site.com/foo/index.html | ||
36 | (or (page-ref pg "url") ; memoization | ||
37 | (let ((url (*urlify (page-path pg)))) | ||
38 | (page-set! pg "url" url) | ||
39 | url))) | ||
40 | |||
41 | (define (*slugify url) ; I don't love how this is written.. | ||
42 | (let-values (((_ _ dirs) (decompose-directory url))) | ||
43 | (let loop ((this (car dirs)) | ||
44 | (rest (cdr dirs))) | ||
45 | (if (null? (cdr rest)) | ||
46 | (make-pathname (list "/" this) #f) | ||
47 | (loop (car rest) | ||
48 | (cdr rest)))))) | ||
49 | |||
50 | (define (page-slug pg) ; http://site.com/foo/index.html => /foo/ | ||
51 | (or (page-ref pg "slug") ; memoization | ||
52 | (let ((slug (*slugify (page-url pg)))) | ||
53 | (page-set! pg "slug" slug) | ||
54 | slug))) | ||
55 | |||
56 | (define (read-page path) | ||
57 | (let ((pg (make-page path #f #f #f #f))) | ||
58 | (set-page-dest! pg ((apply o (page-path-transformers)) path)) | ||
59 | (set-page-text! pg (with-input-from-file path slurp)) | ||
60 | (set-page-sxml! pg ((apply o (page-text-transformers)) (page-text pg))) | ||
61 | (set-page-meta! pg (extract-metadata (page-sxml pg))) | ||
62 | pg)) | ||
63 | |||
64 | (define write-page | ||
65 | (case-lambda | ||
66 | ((pg) (call-with-output-file (page-dest pg) | ||
67 | (lambda (port) (write-page pg port)))) | ||
68 | ((pg port) | ||
69 | (sxml-display-as-html ((eval/q (page-template)) pg) port)))) | ||
70 | |||
71 | |||
72 | |||
diff --git a/lib/util.sld b/lib/util.sld new file mode 100644 index 0000000..64c633e --- /dev/null +++ b/lib/util.sld | |||
@@ -0,0 +1,50 @@ | |||
1 | ;;; (boudin util) --- utility functions | ||
2 | |||
3 | (define-library (boudin util) | ||
4 | (import (scheme base) | ||
5 | (scheme case-lambda) | ||
6 | (scheme eval)) | ||
7 | |||
8 | (export identity | ||
9 | o | ||
10 | assoc-ref | ||
11 | slurp | ||
12 | eval/q) | ||
13 | |||
14 | (begin | ||
15 | (define (identity x) x) | ||
16 | |||
17 | (define (o . procs) ; stole from chicken core | ||
18 | (if (null? procs) | ||
19 | identity | ||
20 | (let loop ((procs procs)) | ||
21 | (let ((h (car procs)) | ||
22 | (t (cdr procs))) | ||
23 | (if (null? t) | ||
24 | h | ||
25 | (lambda (x) (h ((loop t) x)))))))) | ||
26 | |||
27 | (define assoc-ref | ||
28 | (case-lambda | ||
29 | ((key alist) | ||
30 | (assoc-ref alist | ||
31 | key | ||
32 | (lambda () (error "Unrecognized key." key)))) | ||
33 | ((key alist failure) | ||
34 | (cond ((assoc key alist) => cdr) | ||
35 | (else (failure)))))) | ||
36 | |||
37 | (define slurp | ||
38 | (case-lambda | ||
39 | (() (slurp (current-input-port))) | ||
40 | ((port) | ||
41 | (let loop ((ch (read-char)) | ||
42 | (acc '())) | ||
43 | (if (eof-object? ch) | ||
44 | (list->string (reverse acc)) | ||
45 | (loop (read-char) (cons ch acc))))))) | ||
46 | |||
47 | (define (eval/q form env) ; this is probably a bad idea | ||
48 | (eval (list 'quasiquote form) env)) | ||
49 | |||
50 | )) | ||