snarf-check-and-output-texi.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. ;;; snarf-check-and-output-texi --- called by the doc snarfer.
  2. ;; Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
  3. ;;
  4. ;; This program is free software; you can redistribute it and/or
  5. ;; modify it under the terms of the GNU Lesser General Public License
  6. ;; as published by the Free Software Foundation; either version 3, or
  7. ;; (at your option) any later version.
  8. ;;
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Lesser General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Lesser General Public
  15. ;; License along with this software; see the file COPYING.LESSER. If
  16. ;; not, write to the Free Software Foundation, Inc., 51 Franklin
  17. ;; Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. ;;; Author: Michael Livshin
  19. ;;; Code:
  20. (define-module (scripts snarf-check-and-output-texi)
  21. :use-module (ice-9 streams)
  22. :use-module (ice-9 match)
  23. :export (snarf-check-and-output-texi))
  24. ;;; why aren't these in some module?
  25. (define-macro (when cond . body)
  26. `(if ,cond (begin ,@body)))
  27. (define-macro (unless cond . body)
  28. `(if (not ,cond) (begin ,@body)))
  29. (define *manual-flag* #f)
  30. (define (snarf-check-and-output-texi . flags)
  31. (if (member "--manual" flags)
  32. (set! *manual-flag* #t))
  33. (process-stream (current-input-port)))
  34. (define (process-stream port)
  35. (let loop ((input (stream-map (match-lambda
  36. (('id . s)
  37. (cons 'id (string->symbol s)))
  38. (('int_dec . s)
  39. (cons 'int (string->number s)))
  40. (('int_oct . s)
  41. (cons 'int (string->number s 8)))
  42. (('int_hex . s)
  43. (cons 'int (string->number s 16)))
  44. ((and x (? symbol?))
  45. (cons x x))
  46. ((and x (? string?))
  47. (cons 'string x))
  48. (x x))
  49. (make-stream (lambda (s)
  50. (let loop ((s s))
  51. (cond
  52. ((stream-null? s) #t)
  53. ((eq? 'eol (stream-car s))
  54. (loop (stream-cdr s)))
  55. (else (cons (stream-car s) (stream-cdr s))))))
  56. (port->stream port read)))))
  57. (unless (stream-null? input)
  58. (let ((token (stream-car input)))
  59. (if (eq? (car token) 'snarf_cookie)
  60. (dispatch-top-cookie (stream-cdr input)
  61. loop)
  62. (loop (stream-cdr input)))))))
  63. (define (dispatch-top-cookie input cont)
  64. (when (stream-null? input)
  65. (error 'syntax "premature end of file"))
  66. (let ((token (stream-car input)))
  67. (cond
  68. ((eq? (car token) 'brace_open)
  69. (consume-multiline (stream-cdr input)
  70. cont))
  71. (else
  72. (consume-upto-cookie process-singleline
  73. input
  74. cont)))))
  75. (define (consume-upto-cookie process input cont)
  76. (let loop ((acc '()) (input input))
  77. (when (stream-null? input)
  78. (error 'syntax "premature end of file in directive context"))
  79. (let ((token (stream-car input)))
  80. (cond
  81. ((eq? (car token) 'snarf_cookie)
  82. (process (reverse! acc))
  83. (cont (stream-cdr input)))
  84. (else (loop (cons token acc) (stream-cdr input)))))))
  85. (define (consume-multiline input cont)
  86. (begin-multiline)
  87. (let loop ((input input))
  88. (when (stream-null? input)
  89. (error 'syntax "premature end of file in multiline context"))
  90. (let ((token (stream-car input)))
  91. (cond
  92. ((eq? (car token) 'brace_close)
  93. (end-multiline)
  94. (cont (stream-cdr input)))
  95. (else (consume-upto-cookie process-multiline-directive
  96. input
  97. loop))))))
  98. (define *file* #f)
  99. (define *line* #f)
  100. (define *c-function-name* #f)
  101. (define *function-name* #f)
  102. (define *snarf-type* #f)
  103. (define *args* #f)
  104. (define *sig* #f)
  105. (define *docstring* #f)
  106. (define (begin-multiline)
  107. (set! *file* #f)
  108. (set! *line* #f)
  109. (set! *c-function-name* #f)
  110. (set! *function-name* #f)
  111. (set! *snarf-type* #f)
  112. (set! *args* #f)
  113. (set! *sig* #f)
  114. (set! *docstring* #f))
  115. (define *primitive-deffnx-signature* "@deffnx {Scheme Procedure} ")
  116. (define *primitive-deffnx-sig-length* (string-length *primitive-deffnx-signature*))
  117. (define (end-multiline)
  118. (let* ((req (car *sig*))
  119. (opt (cadr *sig*))
  120. (var (caddr *sig*))
  121. (all (+ req opt var)))
  122. (if (and (not (eqv? *snarf-type* 'register))
  123. (not (= (length *args*) all)))
  124. (error (format #f "~A:~A: ~A's C implementation takes ~A args (should take ~A)"
  125. *file* *line* *function-name* (length *args*) all)))
  126. (let ((nice-sig
  127. (if (eq? *snarf-type* 'register)
  128. *function-name*
  129. (with-output-to-string
  130. (lambda ()
  131. (format #t "~A" *function-name*)
  132. (let loop-req ((args *args*) (r 0))
  133. (if (< r req)
  134. (begin
  135. (format #t " ~A" (car args))
  136. (loop-req (cdr args) (+ 1 r)))
  137. (let loop-opt ((o 0) (args args) (tail '()))
  138. (if (< o opt)
  139. (begin
  140. (format #t " [~A" (car args))
  141. (loop-opt (+ 1 o) (cdr args) (cons #\] tail)))
  142. (begin
  143. (if (> var 0)
  144. (format #t " . ~A"
  145. (car args)))
  146. (let loop-tail ((tail tail))
  147. (if (not (null? tail))
  148. (begin
  149. (format #t "~A" (car tail))
  150. (loop-tail (cdr tail))))))))))))))
  151. (scm-deffnx
  152. (if (and *manual-flag* (eq? *snarf-type* 'primitive))
  153. (with-output-to-string
  154. (lambda ()
  155. (format #t "@deffnx {C Function} ~A (" *c-function-name*)
  156. (unless (null? *args*)
  157. (format #t "~A" (car *args*))
  158. (let loop ((args (cdr *args*)))
  159. (unless (null? args)
  160. (format #t ", ~A" (car args))
  161. (loop (cdr args)))))
  162. (format #t ")\n")))
  163. #f)))
  164. (format #t "\n ~A\n" *function-name*)
  165. (format #t "@c snarfed from ~A:~A\n" *file* *line*)
  166. (format #t "@deffn {Scheme Procedure} ~A\n" nice-sig)
  167. (let loop ((strings *docstring*) (scm-deffnx scm-deffnx))
  168. (cond ((null? strings))
  169. ((or (not scm-deffnx)
  170. (and (>= (string-length (car strings))
  171. *primitive-deffnx-sig-length*)
  172. (string=? (substring (car strings)
  173. 0 *primitive-deffnx-sig-length*)
  174. *primitive-deffnx-signature*)))
  175. (display (car strings))
  176. (loop (cdr strings) scm-deffnx))
  177. (else (display scm-deffnx)
  178. (loop strings #f))))
  179. (display "\n")
  180. (display "@end deffn\n"))))
  181. (define (texi-quote s)
  182. (let rec ((i 0))
  183. (if (= i (string-length s))
  184. ""
  185. (string-append (let ((ss (substring s i (+ i 1))))
  186. (if (string=? ss "@")
  187. "@@"
  188. ss))
  189. (rec (+ i 1))))))
  190. (define (process-multiline-directive l)
  191. (define do-args
  192. (match-lambda
  193. (('(paren_close . paren_close))
  194. '())
  195. (('(comma . comma) rest ...)
  196. (do-args rest))
  197. (('(id . SCM) ('id . name) rest ...)
  198. (cons name (do-args rest)))
  199. (x (error (format #f "invalid argument syntax: ~A" (map cdr x))))))
  200. (define do-arglist
  201. (match-lambda
  202. (('(paren_open . paren_open) '(id . void) '(paren_close . paren_close))
  203. '())
  204. (('(paren_open . paren_open) rest ...)
  205. (do-args rest))
  206. (x (error (format #f "invalid arglist syntax: ~A" (map cdr x))))))
  207. (define do-command
  208. (match-lambda
  209. (('cname ('id . name))
  210. (set! *c-function-name* (texi-quote (symbol->string name))))
  211. (('fname ('string . name) ...)
  212. (set! *function-name* (texi-quote (apply string-append name))))
  213. (('type ('id . type))
  214. (set! *snarf-type* type))
  215. (('type ('int . num))
  216. (set! *snarf-type* num))
  217. (('location ('string . file) ('int . line))
  218. (set! *file* file)
  219. (set! *line* line))
  220. ;; newer gccs like to throw around more location markers into the
  221. ;; preprocessed source; these (hash . hash) bits are what they translate to
  222. ;; in snarfy terms.
  223. (('location ('string . file) ('int . line) ('hash . 'hash))
  224. (set! *file* file)
  225. (set! *line* line))
  226. (('location ('hash . 'hash) ('string . file) ('int . line) ('hash . 'hash))
  227. (set! *file* file)
  228. (set! *line* line))
  229. (('arglist rest ...)
  230. (set! *args* (do-arglist rest)))
  231. (('argsig ('int . req) ('int . opt) ('int . var))
  232. (set! *sig* (list req opt var)))
  233. (x (error (format #f "unknown doc attribute: ~A" x)))))
  234. (define do-directive
  235. (match-lambda
  236. ((('id . command) rest ...)
  237. (do-command (cons command rest)))
  238. ((('string . string) ...)
  239. (set! *docstring* string))
  240. (x (error (format #f "unknown doc attribute syntax: ~A" x)))))
  241. (do-directive l))
  242. (define (process-singleline l)
  243. (define do-argpos
  244. (match-lambda
  245. ((('id . name) ('int . pos) ('int . line))
  246. (let ((idx (list-index *args* name)))
  247. (when idx
  248. (unless (= (+ idx 1) pos)
  249. (display (format #f "~A:~A: wrong position for argument ~A: ~A (should be ~A)\n"
  250. *file* line name pos (+ idx 1))
  251. (current-error-port))))))
  252. (x #f)))
  253. (define do-command
  254. (match-lambda
  255. (('(id . argpos) rest ...)
  256. (do-argpos rest))
  257. (x (error (format #f "unknown check: ~A" x)))))
  258. (when *function-name*
  259. (do-command l)))
  260. (define main snarf-check-and-output-texi)