More GOOPS comments
[bpt/guile.git] / module / texinfo / serialize.scm
CommitLineData
47f3ce52
AW
1;;;; (texinfo serialize) -- rendering stexinfo as texinfo
2;;;;
d4cab459 3;;;; Copyright (C) 2009, 2012, 2013 Free Software Foundation, Inc.
47f3ce52
AW
4;;;; Copyright (C) 2003,2004,2009 Andy Wingo <wingo at pobox dot com>
5;;;;
6;;;; This library is free software; you can redistribute it and/or
7;;;; modify it under the terms of the GNU Lesser General Public
8;;;; License as published by the Free Software Foundation; either
9;;;; version 3 of the License, or (at your option) any later version.
10;;;;
11;;;; This library is distributed in the hope that it will be useful,
12;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14;;;; Lesser General Public License for more details.
15;;;;
16;;;; You should have received a copy of the GNU Lesser General Public
17;;;; License along with this library; if not, write to the Free Software
18;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19;;;;
20\f
21;;; Commentary:
22;;
23;;Serialization of @code{stexi} to plain texinfo.
24;;
25;;; Code:
26
27(define-module (texinfo serialize)
28 #:use-module (texinfo)
29 #:use-module (texinfo string-utils)
30 #:use-module (sxml transform)
31 #:use-module (srfi srfi-1)
32 #:use-module (srfi srfi-13)
33 #:export (stexi->texi))
34
35(define (list-intersperse src-l elem)
36 (if (null? src-l) src-l
37 (let loop ((l (cdr src-l)) (dest (cons (car src-l) '())))
38 (if (null? l) (reverse dest)
39 (loop (cdr l) (cons (car l) (cons elem dest)))))))
40
41;; converts improper lists to proper lists.
42(define (filter* pred l)
43 (let lp ((in l) (out '()))
44 (cond ((null? in)
45 (reverse! out))
46 ((pair? in)
47 (lp (cdr in) (if (pred (car in)) (cons (car in) out) out)))
48 (else
49 (lp '() (if (pred in) (cons in out) out))))))
50
51;; (list* 'a '(b c) 'd '(e f g)) => '(a b c d e f g)
52(define (list* . args)
53 (let* ((args (reverse args))
54 (tail (car args)))
55 (let lp ((in (cdr args)) (out tail))
56 (cond ((null? in) out)
57 ((pair? (car in)) (lp (cdr in) (append (car in) out)))
58 ((null? (car in)) (lp (cdr in) out))
59 (else (lp (cdr in) (cons (car in) out)))))))
60
61;; Why? Well, because syntax-case defines `include', and carps about its
62;; wrong usage below...
f6ddf827 63(eval-when (expand load eval)
47f3ce52
AW
64 (define (include exp lp command type formals args accum)
65 (list* "\n"
66 (list-intersperse
67 args
68 " ")
69 " " command "@" accum)))
70
71(define (empty-command exp lp command type formals args accum)
72 (list* " " command "@" accum))
73
74(define (inline-text exp lp command type formals args accum)
75 (if (not (string=? command "*braces*")) ;; fixme :(
76 (list* "}"
77 (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
78 "{" command "@" accum)
79 (list* "@}"
80 (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
81 "@{" accum)))
82
83(define (inline-args exp lp command type formals args accum)
84 (list* "}"
85 (if (not args) ""
86 (list-intersperse
87 (map
88 (lambda (x)
89 (cond ((not x) "")
90 ((pair? x)
91 (if (pair? (cdr x))
92 (warn "Strange inline-args!" args))
93 (car x))
94 (else (error "Invalid inline-args" args))))
95 (drop-while not
96 (map (lambda (x) (assq-ref args x))
97 (reverse formals))))
98 ","))
99 "{" command "@" accum))
100
be52f329
AW
101(define (inline-text-args exp lp command type formals args accum)
102 (list* "}"
103 (if (not args) ""
104 (apply
105 append
106 (list-intersperse
107 (map
108 (lambda (x) (append-map (lambda (x) (lp x '())) (reverse x)))
109 (drop-while not
110 (map (lambda (x) (assq-ref args x))
111 (reverse formals))))
112 '(","))))
113 "{" command "@" accum))
114
47f3ce52
AW
115(define (serialize-text-args lp formals args)
116 (apply
117 append
118 (list-intersperse
119 (map (lambda (arg) (append-map (lambda (x) (lp x '())) arg))
120 (map
121 reverse
122 (drop-while
123 not (map (lambda (x) (assq-ref args x))
124 (reverse formals)))))
125 '(" "))))
126
127(define (eol-text-args exp lp command type formals args accum)
128 (list* "\n"
129 (serialize-text-args lp formals args)
130 " " command "@" accum))
131
132(define (eol-text exp lp command type formals args accum)
133 (list* "\n"
134 (append-map (lambda (x) (lp x '()))
135 (reverse (if args (cddr exp) (cdr exp))))
136 " " command "@" accum))
137
138(define (eol-args exp lp command type formals args accum)
139 (list* "\n"
140 (list-intersperse
141 (apply append
142 (drop-while not
143 (map (lambda (x) (assq-ref args x))
144 (reverse formals))))
145 ", ")
146 " " command "@" accum))
147
148(define (environ exp lp command type formals args accum)
149 (case (car exp)
150 ((texinfo)
151 (list* "@bye\n"
152 (append-map (lambda (x) (lp x '())) (reverse (cddr exp)))
153 "\n@c %**end of header\n\n"
154 (reverse (assq-ref args 'title)) "@settitle "
155 (or (and=> (assq-ref args 'filename)
156 (lambda (filename)
157 (cons "\n" (reverse (cons "@setfilename " filename)))))
158 "")
159 "\\input texinfo @c -*-texinfo-*-\n@c %**start of header\n"
160 accum))
161 (else
162 (list* "\n\n" command "@end "
163 (let ((body (append-map (lambda (x) (lp x '()))
164 (reverse (if args (cddr exp) (cdr exp))))))
165 (if (or (null? body)
166 (eqv? (string-ref (car body)
167 (1- (string-length (car body))))
168 #\newline))
169 body
170 (cons "\n" body)))
171 "\n"
172 (serialize-text-args lp formals args)
173 " " command "@" accum))))
174
175(define (table-environ exp lp command type formals args accum)
176 (list* "\n\n" command "@end "
177 (append-map (lambda (x) (lp x '()))
178 (reverse (if args (cddr exp) (cdr exp))))
179 "\n"
180 (let* ((arg (if args (cadar args) ""))) ;; zero or one args
181 (if (pair? arg)
182 (list (symbol->string (car arg)) "@")
183 arg))
184 " " command "@" accum))
185
186(define (wrap strings)
187 (fill-string (string-concatenate strings)
d4cab459
AW
188 #:line-width 72
189 #:break-long-words? #f))
47f3ce52
AW
190
191(define (paragraph exp lp command type formals args accum)
192 (list* "\n\n"
193 (wrap
194 (reverse
195 (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))))
196 accum))
197
198(define (item exp lp command type formals args accum)
199 (list* (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
200 "@item\n"
201 accum))
202
203(define (entry exp lp command type formals args accum)
204 (list* (append-map (lambda (x) (lp x '())) (reverse (cddr exp)))
205 "\n"
206 (append-map (lambda (x) (lp x '())) (reverse (cdar args)))
207 "@item "
208 accum))
209
210(define (fragment exp lp command type formals args accum)
211 (list* "\n@c %end of fragment\n"
212 (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
213 "\n@c %start of fragment\n\n"
214 accum))
215
216(define serializers
217 `((EMPTY-COMMAND . ,empty-command)
218 (INLINE-TEXT . ,inline-text)
219 (INLINE-ARGS . ,inline-args)
be52f329 220 (INLINE-TEXT-ARGS . ,inline-text-args)
47f3ce52
AW
221 (EOL-TEXT . ,eol-text)
222 (EOL-TEXT-ARGS . ,eol-text-args)
223 (INDEX . ,eol-text-args)
224 (EOL-ARGS . ,eol-args)
225 (ENVIRON . ,environ)
226 (TABLE-ENVIRON . ,table-environ)
227 (ENTRY . ,entry)
228 (ITEM . ,item)
229 (PARAGRAPH . ,paragraph)
230 (FRAGMENT . ,fragment)
231 (#f . ,include))) ; support writing include statements
232
233(define (serialize exp lp command type formals args accum)
234 ((or (assq-ref serializers type)
235 (error "Unknown command type" exp type))
236 exp lp command type formals args accum))
237
238(define escaped-chars '(#\} #\{ #\@))
239(define (escape str)
240 "Escapes any illegal texinfo characters (currently @{, @}, and @@)."
241 (let loop ((in (string->list str)) (out '()))
242 (if (null? in)
243 (apply string (reverse out))
244 (if (memq (car in) escaped-chars)
245 (loop (cdr in) (cons* (car in) #\@ out))
246 (loop (cdr in) (cons (car in) out))))))
247
248(define (stexi->texi tree)
249 "Serialize the stexi @var{tree} into plain texinfo."
250 (string-concatenate-reverse
251 (let lp ((in tree) (out '()))
252 (cond
253 ((or (not in) (null? in)) out)
254 ((string? in) (cons (escape in) out))
255 ((pair? in)
256 (let ((command-spec (assq (car in) texi-command-specs)))
257 (if (not command-spec)
258 (begin
259 (warn "Unknown stexi command, not rendering" in)
260 out)
261 (serialize in
262 lp
263 (symbol->string (car in))
264 (cadr command-spec)
265 (filter* symbol? (cddr command-spec))
266 (cond
267 ((and (pair? (cdr in)) (pair? (cadr in))
268 (eq? (caadr in) '%))
269 (cdadr in))
270 ((not (cadr command-spec))
271 ;; include
272 (cdr in))
273 (else
274 #f))
275 out))))
276 (else
277 (error "Invalid stexi" in))))))
278
279;;; arch-tag: d3fa16ea-0bf7-4ec5-ab9f-3f08490f77f5