File: notangle_guile.in

package info (click to toggle)
libgeda 20050313-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 5,636 kB
  • ctags: 1,505
  • sloc: ansic: 19,449; sh: 8,627; makefile: 197; perl: 59
file content (168 lines) | stat: -rw-r--r-- 6,300 bytes parent folder | download | duplicates (2)
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#! @GUILEINTERP@ \
-e main -s
!#

;;; Copyright (C) 2001, 2005 by Patrick Bernaud. All rights reserved
;;; See file COPYRIGHT for more information.

;;; This scripts is supposed to do the same things as notangle do.
;;; Its purpose it to avoid the need of a complete noweb installation
;;; to simply get the sources from a noweb file, provided the user
;;; has a Guile interpreter.


(use-modules (ice-9 getopt-long)
	     (ice-9 regex))

(debug-enable 'backtrace)

(define noweb-rootname     "*")
(define noweb-filename     #f)
(define code-chunks        '())

(define def-regexp  (make-regexp "^\<\<(.*)\>\>="))
(define ref-regexp  (make-regexp "\<\<([^>]*)\>\>"))
(define end-regexp  (make-regexp "^@\ ?"))


(define (tangle-step1)

  ;; reads and returns a code chunk
  (define (read-code-chunk)
    (let loop ((line (read-line (current-input-port) 'concat)))
      (if (not (eof-object? line))
          ;; code chunk ends at line that match end-regexp (^@\ ?)
          (if (regexp-exec end-regexp line)
              ;; reached end of chunk
              '()
              ;; end not yet reached, continues the analyze
              (append (list line) 
                      (loop (read-line (current-input-port) 'concat)))))))

  ;; identifies code chunk and adds them to the association table
  (let loop ((line ""))
    (if (not (eof-object? line))
        (begin
          (and=> (regexp-exec def-regexp line)
                 (lambda (m)
                   (add-code-chunk (match:substring m 1) (read-code-chunk))))
          (loop (read-line))))))

(define (tangle-step2)

  ;; search in the association table for a code chunk named name
  ;; and output its content recursively, looking for embedded references
  (define (output-code-chunk name offset)
    (let ((code (assoc-ref code-chunks name))
          (first #t))
      ;; check there actually exists a code chunk with this name
      (if code
          ;; yes, go on and process each line of the code chunk
          (for-each
           (lambda (line)
             ;; the offset must not be printed when it is the first line
             ;; of a code chunk
             (if (not first) (display offset) (set! first #f))
             ;; search for one (or more) chunk reference in the current line
             (let loop ((l line))
               (cond ((regexp-exec ref-regexp l)
                      ;; found a reference in the line, process it
                      => (lambda (m)
                           (display (match:prefix m))
                           (output-code-chunk (match:substring m 1)
                                              (string-append offset
                                                             (make-string
                                                              (match:start m)
                                                              #\space)))
                           (loop (match:suffix m))))
                     (else
                      ;; no reference found, output the full line
                      (display l)))))
           code)
          (error (format #f "undefined chunk name: \<\<~a\>\>~%" name)))))

  ;; check there is a code chunk with name rootname
  (if (assoc-ref code-chunks noweb-rootname)
      ;; ok, start tangling from rootname and null offset
      (output-code-chunk noweb-rootname "")
      (error (format #f "The root module \<\<~a\>\> was not defined.~%" 
                     noweb-rootname))))

(define (tangle-noweb)
  ;; first reads the file and collect the code chunks
  (tangle-step1)
  ;; then assemble them
  (tangle-step2))

(define (add-code-chunk name code)
  ;; the last newline char of a code chunk must be removed
  (let ((last (1- (length code))))
    (if (>= last 0)
        (list-set! code last (remove-newline (list-ref code last)))))
  ;; add a new entry for name to the association list code-chunks
  ;; or complete an entry if it already exists
  (set! code-chunks 
        (assoc-set! code-chunks name
                    (let ((code-prev (assoc-ref code-chunks name)))
                      ;; check if chunk is already defined
                      (if code-prev
                          ;; yes, append new code
                          (append code-prev (list "\n") code)
                          ;; otherwise, only associate name with code
                          code)))))

(define (remove-newline str)
  (cond ((string-null? str) str)
        ((char=? (string-ref str (1- (string-length str))) #\newline)
         (substring str 0 (1- (string-length str))))
        (else str)))


(define option-spec
  '((rootname (required? #f) (single-char #\R) (value #t))
    (help     (required? #f) (single-char #\h))
    (version  (required? #f) (single-char #\v))
    ))

(define (display-usage)
  (display "Usage: notangle_guile [options...] [file]\n")
  (display "  --help, -h                  Show this usage information\n")
  (display "  --version, -v               Show version information\n")
  (display "  --rootname=value, -Rvalue   Set root code chunk name to value\n")
  (exit))

(define (display-version)
  (display "notangle_guile 20050224\n")
  (exit))

(define (main args)
  ;; processing command line
  (let* ((opts           (getopt-long args option-spec))
         (help-wanted    (option-ref opts 'help     #f))
         (version-wanted (option-ref opts 'version  #f))
         (rootname       (option-ref opts 'rootname #f))
         (filename       (option-ref opts '()       #f)))
    ;; display version if -v flag
    (if version-wanted (display-version))
    ;; display usage   if -h flag
    (if help-wanted    (display-usage))
    ;; if a filename is given, filename is a string
    ;; else, filename is set to #f
    (if (not (null? filename))
        (if (file-exists? (car filename))
            (set! noweb-filename (car filename))
            (error (format #f "couldn't find file ~a~%" (car filename)))))
    ;; if rootname is given, rootname is a string
    ;; else, rootname is set to #f
    (if (string? rootname)
        (set! noweb-rootname rootname)))

  ;; start tangling
  (if (string? noweb-filename)
      ;; work on a file
      (with-input-from-file noweb-filename
        tangle-noweb)
      ;; no file specified, work on current input port
      (tangle-noweb)))