summaryrefslogtreecommitdiff
path: root/agen5/schemedef.scm
blob: f87f04b1deec183b097e07f03dd9a54c8f2c31a3 (plain)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

;;; Time-stamp:        "2012-04-14 08:39:41 bkorb"
;;;
;;; This file is part of AutoGen.
;;; AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
;;;
;;; AutoGen is free software: you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by the
;;; Free Software Foundation, either version 3 of the License, or
;;; (at your option) any later version.
;;;
;;; AutoGen 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 General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License along
;;; with this program.  If not, see <http://www.gnu.org/licenses/>.
;;;
;;; This module defines all the scheme initialization for AutoGen.
;;; It gets sucked up into directives.h as a single ANSI-C string.
;;; Comments, blank lines and leading white space are omitted.
;;;
;;; The contents of this file get converted to a C language static string
;;; and is then fed to the guile library at start up time.
;;; Blank lines, commends, leading and trailing white space and spaces
;;; before closing parentheses are all stripped.
;;;
(use-modules (ice-9 common-list))

(define identifier?
  (lambda (x) (or (string? x) (symbol? x))))

(define normalize-identifier
  (lambda (x)
    (if (string? x) (string->symbol x) x)))

(define coerce->string
  (lambda (x)
    (let ((char->string (lambda (x) (make-string 1 x)))
          (coercable? (lambda (x)
            (or (string? x) (boolean? x) (char? x)
                (symbol? x) (list? x) (number? x)) )) )

      (if (not (coercable? x))
          (error "Wrong type to coerce->string" x))

      (cond
        ((string? x)  (string-append
            (char->string #\") x (char->string #\")  ))

        ; Probably not what was wanted, but fun
        ((boolean? x) (if x "#t" "#f"))
        ((char? x)    (char->string x))
        ((number? x)  (number->string x))
        ((symbol? x)  (symbol->string x))
        ((list? x)    (if (every coercable? x)
            (apply string-append (map coerce->string x))  ))
) ) ) )


;;; alist->autogen-def:
;;; take a scheme alist of values, and create autogen assignments.
;;; recursive alists are handled. Using a bare list as a value to be
;;; assigned is not a terribly good idea, though it should work if it
;;; doesn't look too much like an alist The returned string doesn't
;;; contain opening and closing brackets.

(define alist->autogen-def
  (lambda (lst . recursive)
    (if (null? recursive) (set! recursive #f)
        (set! recursive #t))
    (let ((res (if recursive "{\n" ""))
          (list-nnul? (lambda (x) (and (list? x) (not (null? x))))))
      (do ((i lst (cdr i)))
          ((null? i) (if recursive
                          (string-append res "}")
                           res))
        (let* ((kvpair (car i))
               (value (cdr kvpair))
               (value-is-alist (if (and (list-nnul? value)
                                        (list-nnul? (car value))
                                        (list-nnul? (caar value))
                                        (identifier? (caaar value)))
                                   #t #f)))
          (set! res (string-append res
                (coerce->string (normalize-identifier (car kvpair)))
                " = "
                (if value-is-alist
                    (alist->autogen-def (car value) 1)
                    (coerce->string (cdr kvpair)))
                ";\n"
) ) ) ) ) )         )

(define shell-cleanup "")
(define add-cleanup (lambda (t)
   (set! shell-cleanup (string-append shell-cleanup "\n" t "\n"))  ))
(define tmp-dir "")

(define header-file     "")
(define header-guard    "")
(define autogen-version "AUTOGEN_VERSION")
(define c-file-line-fmt "#line %2$d \"%1$s\"\n")

(define-macro (defined-as predicate symbol)
  `(and (defined? ',symbol) (,predicate ,symbol)))

;;; /*=gfunc   html_escape_encode
;;;  *
;;;  * what:   encode html special characters
;;;  * general-use:
;;;  *
;;;  * exparg: str , string to make substitutions in
;;;  *
;;;  * doc:    This function will replace replace the characters @code{'&'},
;;;  *         @code{'<'} and @code{'>'} characters with the HTML/XML
;;;  *         escape-encoded strings (@code{"&amp;"}, @code{"&lt;"}, and
;;;  *         @code{"&gt;"}, respectively).
;;; =*/
;;;
(define html-escape-encode (lambda (str)
    (string-substitute str
          '("&"      "<"     ">")
          '("&amp;"  "&lt;"  "&gt;") ) ))

(define stt-table   (make-hash-table 31))
(define stt-curr    stt-table)
(define stt-idx-tbl stt-table)
(define stt-idx     0)

;;; /*=gfunc   string_table_new
;;;  *
;;;  * what:   create a string table
;;;  * general-use:
;;;  *
;;;  * exparg: st-name , the name of the array of characters
;;;  *
;;;  * doc:
;;;  *   This function will create an array of characters.  The companion
;;;  *   functions, (@xref{SCM string-table-add},
;;;  *   @xref{SCM string-table-add-ref}, and
;;;  *   @pxref{SCM emit-string-table}) will insert text and emit the
;;;  *   populated table.
;;;  *
;;;  *   With these functions, it should be much easier to construct
;;;  *   structures containing string offsets instead of string pointers.
;;;  *   That can be very useful when transmitting, storing or sharing data
;;;  *   with different address spaces.
;;;  *
;;;  *   @noindent
;;;  *   Here is a brief example copied from the strtable.test test:
;;;  *
;;;  *   @example
;;;  *      [+ (string-table-new "scribble")
;;;  *    `'   (out-push-new) ;; redirect output to temporary
;;;  *    `'   (define ct 1)  +][+
;;;  *
;;;  *      FOR str IN that was the week that was +][+
;;;  *    `'  (set! ct (+ ct 1))
;;;  *      +]
;;;  *    `'    [+ (string-table-add-ref "scribble" (get "str")) +],[+
;;;  *      ENDFOR  +]
;;;  *      [+ (out-suspend "main")
;;;  *    `'   (emit-string-table "scribble")
;;;  *    `'   (ag-fprintf 0 "\nchar const *ap[%d] = @{" ct)
;;;  *    `'   (out-resume "main")
;;;  *    `'   (out-pop #t) ;; now dump out the redirected output +]
;;;  *    `'    NULL @};
;;;  *   @end example
;;;  *
;;;  *   @noindent
;;;  *   Some explanation:
;;;  *
;;;  *   @noindent
;;;  *   I added the @code{(out-push-new)} because the string table text is
;;;  *   diverted into an output stream named, ``scribble'' and I want to
;;;  *   have the string table emitted before the string table references.
;;;  *   The string table references are also emitted inside the @code{FOR}
;;;  *   loop.  So, when the loop is done, the current output is suspended
;;;  *   under the name, ``main'' and the ``scribble'' table is then emitted
;;;  *   into the primary output.  (@code{emit-string-table} inserts its
;;;  *   output directly into the current output stream.  It does not need to
;;;  *   be the last function in an AutoGen macro block.)  Next I
;;;  *   @code{ag-fprintf} the array-of-pointer declaration directly into the
;;;  *   current output.  Finally I restore the ``main'' output stream and
;;;  *   @code{(out-pop #t)}-it into the main output stream.
;;;  *
;;;  *   Here is the result.  Note that duplicate strings are not repeated
;;;  *   in the string table:
;;;  *
;;;  *   @example
;;;  *      static char const scribble[18] =
;;;  *    `'    "that\0" "was\0"  "the\0"  "week\0";
;;;  *
;;;  *      char const *ap[7] = @{
;;;  *    `'    scribble+0,
;;;  *    `'    scribble+5,
;;;  *    `'    scribble+9,
;;;  *    `'    scribble+13,
;;;  *    `'    scribble+0,
;;;  *    `'    scribble+5,
;;;  *    `'    NULL @};
;;;  *   @end example
;;;  *
;;;  *   These functions use the global name space @code{stt-*} in addition to
;;;  *   the function names.
;;;  *
;;;  *   If you utilize this in your programming, it is recommended that you
;;;  *   prevent printf format usage warnings with the GCC option
;;;  *   @code{-Wno-format-contains-nul}
;;; =*/
;;;
(define string-table-new (lambda (st-name) (begin
   (set! stt-curr (make-hash-table 31))
   (hash-create-handle! stt-table st-name stt-curr)
   (out-push-new)
   (out-suspend st-name)
   (set! stt-idx-tbl (make-hash-table 31))
   (hash-create-handle! stt-curr "string-indexes" stt-idx-tbl)
   (hash-create-handle! stt-curr "current-index"  0)
   ""
)))

;;; /*=gfunc   string_table_add
;;;  * general-use:
;;;  *
;;;  * what:   Add an entry to a string table
;;;  *
;;;  * exparg: st-name , the name of the array of characters
;;;  * exparg: str-val , the (possibly) new value to add
;;;  *
;;;  * doc:    Check for a duplicate string and, if none, then insert a new
;;;  *         string into the string table.  In all cases, returns the
;;;  *         character index of the beginning of the string in the table.
;;;  *
;;;  *         The returned index can be used in expressions like:
;;;  *         @example
;;;  *         string_array + <returned-value>
;;;  *         @end example
;;;  *         @noindent
;;;  *         that will yield the address of the first byte of the inserted
;;;  *         string.  See the @file{strtable.test} AutoGen test for a usage
;;;  *         example.
;;; =*/
;;;
(define string-table-add (lambda (st-name str-val) (begin
   (set! stt-curr    (hash-ref stt-table   st-name))
   (set! stt-idx-tbl (hash-ref stt-curr    "string-indexes"))
   (set! stt-idx     (hash-ref stt-idx-tbl str-val))
   (if (not (number? stt-idx))
       (begin
          (set! stt-idx (hash-ref stt-curr "current-index"))
          (ag-fprintf st-name "/* %5d */ %s \"\\0\"\n"
                      stt-idx (c-string str-val))
          (hash-create-handle! stt-idx-tbl str-val stt-idx)
          (hash-set! stt-curr "current-index"
                    (+ stt-idx (string-length str-val) 1)  )
   )   )
   stt-idx
)))

;;; /*=gfunc   string_table_add_ref
;;;  *
;;;  * what:   Add an entry to a string table, get reference
;;;  * general-use:
;;;  *
;;;  * exparg: st-name , the name of the array of characters
;;;  * exparg: str-val , the (possibly) new value to add
;;;  *
;;;  * doc:    Identical to string-table-add, except the value returned
;;;  *         is the string "st-name" '+' and the index returned by
;;;  *         string-table-add.
;;; =*/
;;;
(define string-table-add-ref (lambda (st-name str-val)
   (string-append st-name "+"
      (number->string (string-table-add st-name str-val)) ) ))

;;; /*=gfunc   emit_string_table
;;;  *
;;;  * what:   output a string table
;;;  *
;;;  * exparg: st-name , the name of the array of characters
;;;  *
;;;  * doc:    Emit into the current output stream a
;;;  *         @code{static char const} array named @code{st-name}
;;;  *         that will have @code{NUL} bytes between each inserted string.
;;; =*/
;;;
(define emit-string-table (lambda (st-name) (begin
   (set! stt-curr (hash-ref stt-table   st-name))
   (set! stt-idx  (hash-ref stt-curr "current-index"))
   (ag-fprintf 0 "\nstatic char const %s[%d] =\n" st-name stt-idx)
   (out-resume st-name)

   ;; Columnize the output.
   ;; Remove any leading spaces -- columns adds them itself.
   ;; Glue the "\0" string to its preceding text.
   ;; End the last line with a semi-colon
   ;;
   (emit (shell (string-append
     "sed 's/^ /      /
	$s/\" \"\\\\0\"/\";/
	s/\" \"\\\\0/\\\\0/
	' <<\\_EOF_\n"
     (out-pop #t)
     "_EOF_")))
   (emit "\n")
)))

;;; /*=gfunc   string_table_size
;;;  *
;;;  * what:   print the current size of a string table
;;;  * general-use:
;;;  *
;;;  * exparg: st-name , the name of the array of characters
;;;  *
;;;  * doc:    Returns the current byte count of the string table.
;;; =*/
;;;
(define string-table-size (lambda (st-name)
  (hash-ref (hash-ref stt-table st-name) "current-index") ))

;;; /*=gfunc   gperf_code
;;;  *
;;;  * what:   emit the source of the generated gperf program
;;;  * general-use:
;;;  *
;;;  * exparg: st-name , the name of the gperf hash list
;;;  *
;;;  * doc:
;;;  *  Returns the contents of the emitted code, suitable
;;;  *  for inclusion in another program.  The interface contains
;;;  *  the following elements:
;;;  *
;;;  *  @table @samp
;;;  *  @item struct @i{<st-name>}_index
;;;  *  containg the fields: @code{@{char const * name, int const id; @};}
;;;  *
;;;  *  @item @i{<st-name>}_hash()
;;;  *  This is the hashing function with local only scope (static).
;;;  *
;;;  *  @item @i{<st-name>}_find()
;;;  *  This is the searching and validation function.  The first argument
;;;  *  is the string to look up, the second is its length.
;;;  *  It returns a pointer to the corresponding @code{@i{<st-name>}_index}
;;;  *  entry.
;;;  *  @end table
;;;  *
;;;  *  Use this in your template as follows where "@i{<st-name>}" was
;;;  *  set to be "@code{lookup}":
;;;  *
;;;  *  @example
;;;  *  [+ (make-gperf "lookup" (join "\n" (stack "name_list")))
;;;  *     (gperf-code "lookup") +]
;;;  *  void my_fun(char * str) @{
;;;  *  struct lookup_index * li = lookup_find(str, strlen(str));
;;;  *  if (li != NULL) printf("%s yields %d\n", str, li->idx);
;;;  *  @end example
;;; =*/
;;;
(define gperf-code (lambda (gp-name) (shellf
  "sed -e '1,/^#line/d' \
       -e '/#include/d' \
       -e '/#line/d' \
       -e '/^[ \t]*$/d' \
       -e 's/^const struct /static const struct /' \
       -e '/^int main(/,$d' ${gpdir}/%s.c"
  gp-name
)))

;;; /*=gfunc   stack_join
;;;  *
;;;  * what:   stack values then join them
;;;  *
;;;  * exparg: join , string between each element
;;;  * exparg: ag-name , name of autogen values to stack
;;;  *
;;;  * doc:    This function will collect all the values named @code{ag-name}
;;;  *         (see the @pxref{SCM stack, stack function}) and join them
;;;  *         separated by the @code{join} string (see the
;;;  *         @pxref{SCM join, join function}).
;;; =*/
;;;
(define stack-join (lambda (j-str ag-name)
  (join j-str (stack ag-name))))

;;; end of agen5/schemedef.scm