mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-29 19:30:36 +02:00
303 lines
9.8 KiB
Scheme
303 lines
9.8 KiB
Scheme
;;; snarf-check-and-output-texi --- called by the doc snarfer.
|
||
|
||
;; Copyright (C) 2001, 2002, 2006, 2011, 2014, 2019 Free Software Foundation, Inc.
|
||
;;
|
||
;; This program is free software; you can redistribute it and/or
|
||
;; modify it under the terms of the GNU Lesser General Public License
|
||
;; as published by the Free Software Foundation; either version 3, or
|
||
;; (at your option) any later version.
|
||
;;
|
||
;; This program is distributed in the hope that it will be useful,
|
||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
;; Lesser General Public License for more details.
|
||
;;
|
||
;; You should have received a copy of the GNU Lesser General Public
|
||
;; License along with this software; see the file COPYING.LESSER. If
|
||
;; not, write to the Free Software Foundation, Inc., 51 Franklin
|
||
;; Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
||
;;; Author: Michael Livshin
|
||
|
||
;;; Code:
|
||
|
||
(define-module (scripts snarf-check-and-output-texi)
|
||
:use-module (ice-9 streams)
|
||
:use-module (ice-9 match)
|
||
:export (snarf-check-and-output-texi))
|
||
|
||
(define %include-in-guild-list #f)
|
||
(define %summary "Transform snarfed .doc files into texinfo documentation.")
|
||
|
||
(define *manual-flag* #f)
|
||
|
||
(define (snarf-check-and-output-texi . flags)
|
||
(if (member "--manual" flags)
|
||
(set! *manual-flag* #t))
|
||
(process-stream (current-input-port)))
|
||
|
||
(define (process-stream port)
|
||
(let loop ((input (stream-map (match-lambda
|
||
(('id . s)
|
||
(cons 'id (string->symbol s)))
|
||
(('int_dec . s)
|
||
(cons 'int (string->number s)))
|
||
(('int_oct . s)
|
||
(cons 'int (string->number s 8)))
|
||
(('int_hex . s)
|
||
(cons 'int (string->number s 16)))
|
||
((and x (? symbol?))
|
||
(cons x x))
|
||
((and x (? string?))
|
||
(cons 'string x))
|
||
(x x))
|
||
(make-stream (lambda (s)
|
||
(let loop ((s s))
|
||
(cond
|
||
((stream-null? s) #t)
|
||
((memq (stream-car s) '(eol hash))
|
||
(loop (stream-cdr s)))
|
||
(else (cons (stream-car s) (stream-cdr s))))))
|
||
(port->stream port read)))))
|
||
|
||
(unless (stream-null? input)
|
||
(let ((token (stream-car input)))
|
||
(if (eq? (car token) 'snarf_cookie)
|
||
(dispatch-top-cookie (stream-cdr input)
|
||
loop)
|
||
(loop (stream-cdr input)))))))
|
||
|
||
(define (dispatch-top-cookie input cont)
|
||
|
||
(when (stream-null? input)
|
||
(error 'syntax "premature end of file"))
|
||
|
||
(let ((token (stream-car input)))
|
||
(cond
|
||
((eq? (car token) 'brace_open)
|
||
(consume-multiline (stream-cdr input)
|
||
cont))
|
||
(else
|
||
(consume-upto-cookie process-singleline
|
||
input
|
||
cont)))))
|
||
|
||
(define (consume-upto-cookie process input cont)
|
||
(let loop ((acc '()) (input input))
|
||
|
||
(when (stream-null? input)
|
||
(error 'syntax "premature end of file in directive context"))
|
||
|
||
(let ((token (stream-car input)))
|
||
(cond
|
||
((eq? (car token) 'snarf_cookie)
|
||
(process (reverse! acc))
|
||
(cont (stream-cdr input)))
|
||
|
||
(else (loop (cons token acc) (stream-cdr input)))))))
|
||
|
||
(define (consume-multiline input cont)
|
||
(begin-multiline)
|
||
|
||
(let loop ((input input))
|
||
|
||
(when (stream-null? input)
|
||
(error 'syntax "premature end of file in multiline context"))
|
||
|
||
(let ((token (stream-car input)))
|
||
(cond
|
||
((eq? (car token) 'brace_close)
|
||
(end-multiline)
|
||
(cont (stream-cdr input)))
|
||
|
||
(else (consume-upto-cookie process-multiline-directive
|
||
input
|
||
loop))))))
|
||
|
||
(define *file* #f)
|
||
(define *line* #f)
|
||
(define *c-function-name* #f)
|
||
(define *function-name* #f)
|
||
(define *snarf-type* #f)
|
||
(define *args* #f)
|
||
(define *sig* #f)
|
||
(define *docstring* #f)
|
||
|
||
(define (begin-multiline)
|
||
(set! *file* #f)
|
||
(set! *line* #f)
|
||
(set! *c-function-name* #f)
|
||
(set! *function-name* #f)
|
||
(set! *snarf-type* #f)
|
||
(set! *args* #f)
|
||
(set! *sig* #f)
|
||
(set! *docstring* #f))
|
||
|
||
(define *primitive-deffnx-signature* "@deffnx {Scheme Procedure} ")
|
||
(define *primitive-deffnx-sig-length* (string-length *primitive-deffnx-signature*))
|
||
|
||
(define (end-multiline)
|
||
(let* ((req (car *sig*))
|
||
(opt (cadr *sig*))
|
||
(var (caddr *sig*))
|
||
(all (+ req opt var)))
|
||
(if (and (not (eqv? *snarf-type* 'register))
|
||
(not (= (length *args*) all)))
|
||
(error (format #f "~A:~A: ~A's C implementation takes ~A args (should take ~A)"
|
||
*file* *line* *function-name* (length *args*) all)))
|
||
(let ((nice-sig
|
||
(if (eq? *snarf-type* 'register)
|
||
*function-name*
|
||
(with-output-to-string
|
||
(lambda ()
|
||
(format #t "~A" *function-name*)
|
||
(let loop-req ((args *args*) (r 0))
|
||
(if (< r req)
|
||
(begin
|
||
(format #t " ~A" (car args))
|
||
(loop-req (cdr args) (+ 1 r)))
|
||
(let loop-opt ((o 0) (args args) (tail '()))
|
||
(if (< o opt)
|
||
(begin
|
||
(format #t " [~A" (car args))
|
||
(loop-opt (+ 1 o) (cdr args) (cons #\] tail)))
|
||
(begin
|
||
(if (> var 0)
|
||
(format #t " . ~A"
|
||
(car args)))
|
||
(let loop-tail ((tail tail))
|
||
(if (not (null? tail))
|
||
(begin
|
||
(format #t "~A" (car tail))
|
||
(loop-tail (cdr tail))))))))))))))
|
||
(scm-deffnx
|
||
(if (and *manual-flag* (eq? *snarf-type* 'primitive))
|
||
(with-output-to-string
|
||
(lambda ()
|
||
(format #t "@deffnx {C Function} ~A (" *c-function-name*)
|
||
(unless (null? *args*)
|
||
(format #t "~A" (car *args*))
|
||
(let loop ((args (cdr *args*)))
|
||
(unless (null? args)
|
||
(format #t ", ~A" (car args))
|
||
(loop (cdr args)))))
|
||
(format #t ")\n")))
|
||
#f)))
|
||
(format #t "\n~A\n" *function-name*)
|
||
(format #t "@c snarfed from ~A:~A\n" *file* *line*)
|
||
(format #t "@deffn {Scheme Procedure} ~A\n" nice-sig)
|
||
(let loop ((strings *docstring*) (scm-deffnx scm-deffnx))
|
||
(cond ((null? strings))
|
||
((or (not scm-deffnx)
|
||
(and (>= (string-length (car strings))
|
||
*primitive-deffnx-sig-length*)
|
||
(string=? (substring (car strings)
|
||
0 *primitive-deffnx-sig-length*)
|
||
*primitive-deffnx-signature*)))
|
||
(display (car strings))
|
||
(loop (cdr strings) scm-deffnx))
|
||
(else (display scm-deffnx)
|
||
(loop strings #f))))
|
||
(display "\n")
|
||
(display "@end deffn\n"))))
|
||
|
||
(define (texi-quote s)
|
||
(let rec ((i 0))
|
||
(if (= i (string-length s))
|
||
""
|
||
(string-append (let ((ss (substring s i (+ i 1))))
|
||
(if (string=? ss "@")
|
||
"@@"
|
||
ss))
|
||
(rec (+ i 1))))))
|
||
|
||
(define (process-multiline-directive l)
|
||
|
||
(define do-args
|
||
(match-lambda
|
||
|
||
(('(paren_close . paren_close))
|
||
'())
|
||
|
||
(('(comma . comma) rest ...)
|
||
(do-args rest))
|
||
|
||
(('(id . SCM) ('id . name) rest ...)
|
||
(cons name (do-args rest)))
|
||
|
||
(x (error (format #f "invalid argument syntax: ~A" (map cdr x))))))
|
||
|
||
(define do-arglist
|
||
(match-lambda
|
||
|
||
(('(paren_open . paren_open) '(id . void) '(paren_close . paren_close))
|
||
'())
|
||
|
||
(('(paren_open . paren_open) rest ...)
|
||
(do-args rest))
|
||
|
||
(x (error (format #f "invalid arglist syntax: ~A" (map cdr x))))))
|
||
|
||
(define do-command
|
||
(match-lambda
|
||
|
||
(('cname ('id . name))
|
||
(set! *c-function-name* (texi-quote (symbol->string name))))
|
||
|
||
(('fname ('string . name) ...)
|
||
(set! *function-name* (texi-quote (apply string-append name))))
|
||
|
||
(('type ('id . type))
|
||
(set! *snarf-type* type))
|
||
|
||
(('type ('int . num))
|
||
(set! *snarf-type* num))
|
||
|
||
(('location ('string . file) ('int . line))
|
||
(set! *file* file)
|
||
(set! *line* line))
|
||
|
||
(('arglist rest ...)
|
||
(set! *args* (do-arglist rest)))
|
||
|
||
(('argsig ('int . req) ('int . opt) ('int . var))
|
||
(set! *sig* (list req opt var)))
|
||
|
||
(x (error (format #f "unknown doc attribute: ~A" x)))))
|
||
|
||
(define do-directive
|
||
(match-lambda
|
||
|
||
((('id . command) rest ...)
|
||
(do-command (cons command rest)))
|
||
|
||
((('string . string) ...)
|
||
(set! *docstring* string))
|
||
|
||
(x (error (format #f "unknown doc attribute syntax: ~A" x)))))
|
||
|
||
(do-directive l))
|
||
|
||
(define (process-singleline l)
|
||
|
||
(define do-argpos
|
||
(match-lambda
|
||
((('id . name) ('int . pos) ('int . line))
|
||
(let ((idx (list-index *args* name)))
|
||
(when idx
|
||
(unless (= (+ idx 1) pos)
|
||
(display (format #f "~A:~A: wrong position for argument ~A: ~A (should be ~A)\n"
|
||
*file* line name pos (+ idx 1))
|
||
(current-error-port))))))
|
||
(x #f)))
|
||
|
||
(define do-command
|
||
(match-lambda
|
||
(('(id . argpos) rest ...)
|
||
(do-argpos rest))
|
||
(x (error (format #f "unknown check: ~A" x)))))
|
||
|
||
(when *function-name*
|
||
(do-command l)))
|
||
|
||
(define main snarf-check-and-output-texi)
|