blob: e47ad6737661948078502ac8b20c839ed9bfd2eb (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
;;; CHICKEN-SCRATCH --- heredocs for CHICKEN
;; written by Case Duckworth <acdw@acdw.net> off an idea by evhan
;; Licensed under BSD-3. See COPYING for details.
;; CHICKEN has "Multiline string constants with embedded expressions" syntax,
;; which is basically shell here-doc syntax but schemier and with a real
;; programming langugage to embed. evhan's beaker tool (which is great, btw)
;; uses this facility to do a quick-and-dirty templating for wiki generation. I
;; realized that I could use the same facility for the same kind of
;; heredoc-style templating I have done in various other SSGs like unk and
;; vienna, but with scheme. Thus, CHICKEN-SCRATCH was born.
;; USAGE
;; `expand-string' is the main entry point to this module. It takes a string and
;; returns a string with all #( ... ) forms expanded according to the CHICKEN
;; rules. `expand-port' is a port version of `expand-string'.
;; To enable truly invisible definitions within the expanded string, the `def'
;; macro is provided which performs a `set!' on its variables, then returns a
;; string guaranteed not to be in the input string, which is then filtered out
;; in the expanded string.
;; Finally, to enable CHICKEN-SCRATCH to be used in a shebang, if the first line
;; of the input string begins with #!, it's deleted from the input.
(module chicken-scratch
(expand-string
expand-port
def
%def/replacer)
(import scheme
(chicken base)
(only (chicken io)
read-string)
(only (chicken irregex)
irregex-replace
irregex-replace/all
irregex-search)
(only (chicken port)
make-concatenated-port)
(only (chicken random)
pseudo-random-real))
(define %def/replacer (make-parameter #f))
(define (expand-string str)
(parameterize ((%def/replacer (random-string-not-in str)))
(let* ((delim (random-string-not-in str))
(template (make-concatenated-port
(open-input-string (string-append "#<#" delim "\n"))
(open-input-string (irregex-replace "^#!.*\n" str ""))
(open-input-string (string-append "\n" delim "\n"))))
(expanded (open-output-string))
(output (begin
(display (eval (read template)) expanded)
(get-output-string expanded))))
(irregex-replace/all `(seq ,(%def/replacer) (* "\n"))
output
""))))
(define (expand-port #!optional port)
(let ((port (or port (current-input-port))))
(expand-string (read-string #f port))))
(define-syntax def
(syntax-rules ()
((def var val)
;; I think this only works in CHICKEN.
(begin (set! var val)
(%def/replacer)))))
(define (random-string-not-in str)
(let ((attempt (number->string (pseudo-random-real))))
(if (irregex-search attempt str)
(random-string-not-in str)
attempt))))
|