serialize.scm 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. ;;;; (texinfo serialize) -- rendering stexinfo as texinfo
  2. ;;;;
  3. ;;;; Copyright (C) 2009 Free Software Foundation, Inc.
  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. ;;; Commentary:
  21. ;;
  22. ;;Serialization of @code{stexi} to plain texinfo.
  23. ;;
  24. ;;; Code:
  25. (define-module (texinfo serialize)
  26. #:use-module (texinfo)
  27. #:use-module (texinfo string-utils)
  28. #:use-module (sxml transform)
  29. #:use-module (srfi srfi-1)
  30. #:use-module (srfi srfi-13)
  31. #:export (stexi->texi))
  32. (define (list-intersperse src-l elem)
  33. (if (null? src-l) src-l
  34. (let loop ((l (cdr src-l)) (dest (cons (car src-l) '())))
  35. (if (null? l) (reverse dest)
  36. (loop (cdr l) (cons (car l) (cons elem dest)))))))
  37. ;; converts improper lists to proper lists.
  38. (define (filter* pred l)
  39. (let lp ((in l) (out '()))
  40. (cond ((null? in)
  41. (reverse! out))
  42. ((pair? in)
  43. (lp (cdr in) (if (pred (car in)) (cons (car in) out) out)))
  44. (else
  45. (lp '() (if (pred in) (cons in out) out))))))
  46. ;; (list* 'a '(b c) 'd '(e f g)) => '(a b c d e f g)
  47. (define (list* . args)
  48. (let* ((args (reverse args))
  49. (tail (car args)))
  50. (let lp ((in (cdr args)) (out tail))
  51. (cond ((null? in) out)
  52. ((pair? (car in)) (lp (cdr in) (append (car in) out)))
  53. ((null? (car in)) (lp (cdr in) out))
  54. (else (lp (cdr in) (cons (car in) out)))))))
  55. ;; Why? Well, because syntax-case defines `include', and carps about its
  56. ;; wrong usage below...
  57. (eval-when (eval load compile)
  58. (define (include exp lp command type formals args accum)
  59. (list* "\n"
  60. (list-intersperse
  61. args
  62. " ")
  63. " " command "@" accum)))
  64. (define (empty-command exp lp command type formals args accum)
  65. (list* " " command "@" accum))
  66. (define (inline-text exp lp command type formals args accum)
  67. (if (not (string=? command "*braces*")) ;; fixme :(
  68. (list* "}"
  69. (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
  70. "{" command "@" accum)
  71. (list* "@}"
  72. (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
  73. "@{" accum)))
  74. (define (inline-args exp lp command type formals args accum)
  75. (list* "}"
  76. (if (not args) ""
  77. (list-intersperse
  78. (map
  79. (lambda (x)
  80. (cond ((not x) "")
  81. ((pair? x)
  82. (if (pair? (cdr x))
  83. (warn "Strange inline-args!" args))
  84. (car x))
  85. (else (error "Invalid inline-args" args))))
  86. (drop-while not
  87. (map (lambda (x) (assq-ref args x))
  88. (reverse formals))))
  89. ","))
  90. "{" command "@" accum))
  91. (define (serialize-text-args lp formals args)
  92. (apply
  93. append
  94. (list-intersperse
  95. (map (lambda (arg) (append-map (lambda (x) (lp x '())) arg))
  96. (map
  97. reverse
  98. (drop-while
  99. not (map (lambda (x) (assq-ref args x))
  100. (reverse formals)))))
  101. '(" "))))
  102. (define (eol-text-args exp lp command type formals args accum)
  103. (list* "\n"
  104. (serialize-text-args lp formals args)
  105. " " command "@" accum))
  106. (define (eol-text exp lp command type formals args accum)
  107. (list* "\n"
  108. (append-map (lambda (x) (lp x '()))
  109. (reverse (if args (cddr exp) (cdr exp))))
  110. " " command "@" accum))
  111. (define (eol-args exp lp command type formals args accum)
  112. (list* "\n"
  113. (list-intersperse
  114. (apply append
  115. (drop-while not
  116. (map (lambda (x) (assq-ref args x))
  117. (reverse formals))))
  118. ", ")
  119. " " command "@" accum))
  120. (define (environ exp lp command type formals args accum)
  121. (case (car exp)
  122. ((texinfo)
  123. (list* "@bye\n"
  124. (append-map (lambda (x) (lp x '())) (reverse (cddr exp)))
  125. "\n@c %**end of header\n\n"
  126. (reverse (assq-ref args 'title)) "@settitle "
  127. (or (and=> (assq-ref args 'filename)
  128. (lambda (filename)
  129. (cons "\n" (reverse (cons "@setfilename " filename)))))
  130. "")
  131. "\\input texinfo @c -*-texinfo-*-\n@c %**start of header\n"
  132. accum))
  133. (else
  134. (list* "\n\n" command "@end "
  135. (let ((body (append-map (lambda (x) (lp x '()))
  136. (reverse (if args (cddr exp) (cdr exp))))))
  137. (if (or (null? body)
  138. (eqv? (string-ref (car body)
  139. (1- (string-length (car body))))
  140. #\newline))
  141. body
  142. (cons "\n" body)))
  143. "\n"
  144. (serialize-text-args lp formals args)
  145. " " command "@" accum))))
  146. (define (table-environ exp lp command type formals args accum)
  147. (list* "\n\n" command "@end "
  148. (append-map (lambda (x) (lp x '()))
  149. (reverse (if args (cddr exp) (cdr exp))))
  150. "\n"
  151. (let* ((arg (if args (cadar args) ""))) ;; zero or one args
  152. (if (pair? arg)
  153. (list (symbol->string (car arg)) "@")
  154. arg))
  155. " " command "@" accum))
  156. (define (wrap strings)
  157. (fill-string (string-concatenate strings)
  158. #:line-width 72))
  159. (define (paragraph exp lp command type formals args accum)
  160. (list* "\n\n"
  161. (wrap
  162. (reverse
  163. (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))))
  164. accum))
  165. (define (item exp lp command type formals args accum)
  166. (list* (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
  167. "@item\n"
  168. accum))
  169. (define (entry exp lp command type formals args accum)
  170. (list* (append-map (lambda (x) (lp x '())) (reverse (cddr exp)))
  171. "\n"
  172. (append-map (lambda (x) (lp x '())) (reverse (cdar args)))
  173. "@item "
  174. accum))
  175. (define (fragment exp lp command type formals args accum)
  176. (list* "\n@c %end of fragment\n"
  177. (append-map (lambda (x) (lp x '())) (reverse (cdr exp)))
  178. "\n@c %start of fragment\n\n"
  179. accum))
  180. (define serializers
  181. `((EMPTY-COMMAND . ,empty-command)
  182. (INLINE-TEXT . ,inline-text)
  183. (INLINE-ARGS . ,inline-args)
  184. (EOL-TEXT . ,eol-text)
  185. (EOL-TEXT-ARGS . ,eol-text-args)
  186. (INDEX . ,eol-text-args)
  187. (EOL-ARGS . ,eol-args)
  188. (ENVIRON . ,environ)
  189. (TABLE-ENVIRON . ,table-environ)
  190. (ENTRY . ,entry)
  191. (ITEM . ,item)
  192. (PARAGRAPH . ,paragraph)
  193. (FRAGMENT . ,fragment)
  194. (#f . ,include))) ; support writing include statements
  195. (define (serialize exp lp command type formals args accum)
  196. ((or (assq-ref serializers type)
  197. (error "Unknown command type" exp type))
  198. exp lp command type formals args accum))
  199. (define escaped-chars '(#\} #\{ #\@))
  200. (define (escape str)
  201. "Escapes any illegal texinfo characters (currently @{, @}, and @@)."
  202. (let loop ((in (string->list str)) (out '()))
  203. (if (null? in)
  204. (apply string (reverse out))
  205. (if (memq (car in) escaped-chars)
  206. (loop (cdr in) (cons* (car in) #\@ out))
  207. (loop (cdr in) (cons (car in) out))))))
  208. (define (stexi->texi tree)
  209. "Serialize the stexi @var{tree} into plain texinfo."
  210. (string-concatenate-reverse
  211. (let lp ((in tree) (out '()))
  212. (cond
  213. ((or (not in) (null? in)) out)
  214. ((string? in) (cons (escape in) out))
  215. ((pair? in)
  216. (let ((command-spec (assq (car in) texi-command-specs)))
  217. (if (not command-spec)
  218. (begin
  219. (warn "Unknown stexi command, not rendering" in)
  220. out)
  221. (serialize in
  222. lp
  223. (symbol->string (car in))
  224. (cadr command-spec)
  225. (filter* symbol? (cddr command-spec))
  226. (cond
  227. ((and (pair? (cdr in)) (pair? (cadr in))
  228. (eq? (caadr in) '%))
  229. (cdadr in))
  230. ((not (cadr command-spec))
  231. ;; include
  232. (cdr in))
  233. (else
  234. #f))
  235. out))))
  236. (else
  237. (error "Invalid stexi" in))))))
  238. ;;; arch-tag: d3fa16ea-0bf7-4ec5-ab9f-3f08490f77f5