fcc211b7d35981bd65f47677227084f7483f5987
[bpt/guile.git] / libguile / read.c
1 /* Copyright (C) 1995,1996,1997 Free Software Foundation, Inc.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
40 * If you do not wish that, delete this exception notice. */
41 \f
42
43 #include <stdio.h>
44 #include "_scm.h"
45 #include "chars.h"
46 #include "genio.h"
47 #include "eval.h"
48 #include "unif.h"
49 #include "keywords.h"
50 #include "alist.h"
51 #include "srcprop.h"
52 #include "hashtab.h"
53 #include "hash.h"
54
55 #include "read.h"
56
57 \f
58
59 SCM_SYMBOL (scm_keyword_prefix, "prefix");
60
61 scm_option scm_read_opts[] = {
62 { SCM_OPTION_BOOLEAN, "copy", 0,
63 "Copy source code expressions." },
64 { SCM_OPTION_BOOLEAN, "positions", 0,
65 "Record positions of source code expressions." },
66 { SCM_OPTION_BOOLEAN, "case-insensitive", 0,
67 "Convert symbols to lower case."},
68 { SCM_OPTION_SCM, "keywords", SCM_BOOL_F,
69 "Style of keyword recognition: #f or 'prefix"}
70 };
71
72 SCM_PROC (s_read_options, "read-options-interface", 0, 1, 0, scm_read_options);
73
74 SCM
75 scm_read_options (setting)
76 SCM setting;
77 {
78 SCM ans = scm_options (setting,
79 scm_read_opts,
80 SCM_N_READ_OPTIONS,
81 s_read_options);
82 if (SCM_COPY_SOURCE_P)
83 SCM_RECORD_POSITIONS_P = 1;
84 return ans;
85 }
86
87 /* An association list mapping extra hash characters to procedures. */
88 static SCM *scm_read_hash_procedures;
89
90 SCM_PROC (s_read, "read", 0, 1, 0, scm_read);
91
92 SCM
93 scm_read (port)
94 SCM port;
95 {
96 int c;
97 SCM tok_buf, copy;
98
99 if (SCM_UNBNDP (port))
100 port = scm_cur_inp;
101 else
102 SCM_ASSERT (SCM_NIMP (port) && SCM_OPINPORTP (port),
103 port,
104 SCM_ARG1,
105 s_read);
106
107 c = scm_flush_ws (port, (char *) NULL);
108 if (EOF == c)
109 return SCM_EOF_VAL;
110 scm_ungetc (c, port);
111
112 tok_buf = scm_makstr (30L, 0);
113 return scm_lreadr (&tok_buf, port, &copy);
114 }
115
116
117
118 char *
119 scm_grow_tok_buf (tok_buf)
120 SCM * tok_buf;
121 {
122 scm_vector_set_length_x (*tok_buf, SCM_MAKINUM (2 * SCM_LENGTH (*tok_buf)));
123 return SCM_CHARS (*tok_buf);
124 }
125
126
127
128 int
129 scm_flush_ws (port, eoferr)
130 SCM port;
131 const char *eoferr;
132 {
133 register int c;
134 while (1)
135 switch (c = scm_getc (port))
136 {
137 case EOF:
138 goteof:
139 if (eoferr)
140 scm_wta (SCM_UNDEFINED, "end of file in ", eoferr);
141 return c;
142 case ';':
143 lp:
144 switch (c = scm_getc (port))
145 {
146 case EOF:
147 goto goteof;
148 default:
149 goto lp;
150 case SCM_LINE_INCREMENTORS:
151 break;
152 }
153 break;
154 case SCM_LINE_INCREMENTORS:
155 case SCM_SINGLE_SPACES:
156 case '\t':
157 break;
158 default:
159 return c;
160 }
161 }
162
163
164
165 int
166 scm_casei_streq (s1, s2)
167 char * s1;
168 char * s2;
169 {
170 while (*s1 && *s2)
171 if (scm_downcase((int)*s1) != scm_downcase((int)*s2))
172 return 0;
173 else
174 {
175 ++s1;
176 ++s2;
177 }
178 return !(*s1 || *s2);
179 }
180
181
182 /* recsexpr is used when recording expressions
183 * constructed by read:sharp.
184 */
185 #ifndef DEBUG_EXTENSIONS
186 #define recsexpr(obj, line, column, filename) (obj)
187 #else
188 static SCM recsexpr SCM_P ((SCM obj, int line, int column, SCM filename));
189
190 static SCM
191 recsexpr (obj, line, column, filename)
192 SCM obj;
193 int line;
194 int column;
195 SCM filename;
196 {
197 if (SCM_IMP (obj) || SCM_NCONSP(obj))
198 return obj;
199 {
200 SCM tmp = obj, copy;
201 /* If this sexpr is visible in the read:sharp source, we want to
202 keep that information, so only record non-constant cons cells
203 which haven't previously been read by the reader. */
204 if (SCM_FALSEP (scm_whash_lookup (scm_source_whash, obj)))
205 {
206 if (SCM_COPY_SOURCE_P)
207 {
208 copy = scm_cons (recsexpr (SCM_CAR (obj), line, column, filename),
209 SCM_UNDEFINED);
210 while (SCM_NIMP (tmp = SCM_CDR (tmp)) && SCM_CONSP (tmp))
211 {
212 SCM_SETCDR (copy, scm_cons (recsexpr (SCM_CAR (tmp),
213 line,
214 column,
215 filename),
216 SCM_UNDEFINED));
217 copy = SCM_CDR (copy);
218 }
219 SCM_SETCDR (copy, tmp);
220 }
221 else
222 {
223 recsexpr (SCM_CAR (obj), line, column, filename);
224 while (SCM_NIMP (tmp = SCM_CDR (tmp)) && SCM_CONSP (tmp))
225 recsexpr (SCM_CAR (tmp), line, column, filename);
226 copy = SCM_UNDEFINED;
227 }
228 scm_whash_insert (scm_source_whash,
229 obj,
230 scm_make_srcprops (line,
231 column,
232 filename,
233 copy,
234 SCM_EOL));
235 }
236 return obj;
237 }
238 }
239 #endif
240
241 /* Consume an SCSH-style block comment. Assume that we've already
242 read the initial `#!', and eat characters until we get a
243 newline/exclamation-point/sharp-sign/newline sequence. */
244
245 static void
246 skip_scsh_block_comment (port)
247 SCM port;
248 {
249 /* Is this portable? Dear God, spare me from the non-eight-bit
250 characters. But is it tasteful? */
251 long history = 0;
252
253 for (;;)
254 {
255 int c = scm_getc (port);
256
257 if (c == EOF)
258 scm_wta (SCM_UNDEFINED,
259 "unterminated `#! ... !#' comment", "read");
260 history = ((history << 8) | (c & 0xff)) & 0xffffffff;
261
262 /* Were the last four characters read "\n!#\n"? */
263 if (history == (('\n' << 24) | ('!' << 16) | ('#' << 8) | '\n'))
264 return;
265 }
266 }
267
268 static SCM
269 scm_get_hash_procedure SCM_P ((int c));
270
271 static char s_list[]="list";
272
273 SCM
274 scm_lreadr (tok_buf, port, copy)
275 SCM *tok_buf;
276 SCM port;
277 SCM *copy;
278 {
279 int c;
280 scm_sizet j;
281 SCM p;
282
283 tryagain:
284 c = scm_flush_ws (port, s_read);
285 tryagain_no_flush_ws:
286 switch (c)
287 {
288 case EOF:
289 return SCM_EOF_VAL;
290
291 case '(':
292 return SCM_RECORD_POSITIONS_P
293 ? scm_lreadrecparen (tok_buf, port, s_list, copy)
294 : scm_lreadparen (tok_buf, port, s_list, copy);
295 case ')':
296 scm_wta (SCM_UNDEFINED, "unexpected \")\"", "read");
297 goto tryagain;
298
299 case '\'':
300 p = scm_i_quote;
301 goto recquote;
302 case '`':
303 p = scm_i_quasiquote;
304 goto recquote;
305 case ',':
306 c = scm_getc (port);
307 if ('@' == c)
308 p = scm_i_uq_splicing;
309 else
310 {
311 scm_ungetc (c, port);
312 p = scm_i_unquote;
313 }
314 recquote:
315 p = scm_cons2 (p,
316 scm_lreadr (tok_buf, port, copy),
317 SCM_EOL);
318 if (SCM_RECORD_POSITIONS_P)
319 scm_whash_insert (scm_source_whash,
320 p,
321 scm_make_srcprops (SCM_LINUM (port),
322 SCM_COL (port) - 1,
323 SCM_FILENAME (port),
324 SCM_COPY_SOURCE_P
325 ? (*copy = scm_cons2 (SCM_CAR (p),
326 SCM_CAR (SCM_CDR (p)),
327 SCM_EOL))
328 : SCM_UNDEFINED,
329 SCM_EOL));
330 return p;
331 case '#':
332 c = scm_getc (port);
333 switch (c)
334 {
335 case '(':
336 p = scm_lreadparen (tok_buf, port, "vector", copy);
337 return SCM_NULLP (p) ? scm_nullvect : scm_vector (p);
338
339 case 't':
340 case 'T':
341 return SCM_BOOL_T;
342 case 'f':
343 case 'F':
344 return SCM_BOOL_F;
345
346 case 'b':
347 case 'B':
348 case 'o':
349 case 'O':
350 case 'd':
351 case 'D':
352 case 'x':
353 case 'X':
354 case 'i':
355 case 'I':
356 case 'e':
357 case 'E':
358 scm_ungetc (c, port);
359 c = '#';
360 goto num;
361
362 case '!':
363 /* start of a shell script. Parse as a block comment,
364 terminated by !#, just like SCSH. */
365 skip_scsh_block_comment (port);
366 /* EOF is not an error here */
367 c = scm_flush_ws (port, (char *)NULL);
368 goto tryagain_no_flush_ws;
369
370 case '*':
371 j = scm_read_token (c, tok_buf, port, 0);
372 p = scm_istr2bve (SCM_CHARS (*tok_buf) + 1, (long) (j - 1));
373 if (SCM_NFALSEP (p))
374 return p;
375 else
376 goto unkshrp;
377
378 case '{':
379 j = scm_read_token (c, tok_buf, port, 1);
380 p = scm_intern (SCM_CHARS (*tok_buf), j);
381 return SCM_CAR (p);
382
383 case '\\':
384 c = scm_getc (port);
385 j = scm_read_token (c, tok_buf, port, 0);
386 if (j == 1)
387 return SCM_MAKICHR (c);
388 if (c >= '0' && c < '8')
389 {
390 p = scm_istr2int (SCM_CHARS (*tok_buf), (long) j, 8);
391 if (SCM_NFALSEP (p))
392 return SCM_MAKICHR (SCM_INUM (p));
393 }
394 for (c = 0; c < scm_n_charnames; c++)
395 if (scm_charnames[c]
396 && (scm_casei_streq (scm_charnames[c], SCM_CHARS (*tok_buf))))
397 return SCM_MAKICHR (scm_charnums[c]);
398 scm_wta (SCM_UNDEFINED, "unknown # object: #\\", SCM_CHARS (*tok_buf));
399
400 /* #:SYMBOL is a syntax for keywords supported in all contexts. */
401 case ':':
402 j = scm_read_token ('-', tok_buf, port, 0);
403 p = scm_intern (SCM_CHARS (*tok_buf), j);
404 return scm_make_keyword_from_dash_symbol (SCM_CAR (p));
405
406 default:
407 callshrp:
408 {
409 SCM sharp = scm_get_hash_procedure (c);
410
411 if (SCM_NIMP (sharp))
412 {
413 int line = SCM_LINUM (port);
414 int column = SCM_COL (port) - 2;
415 SCM got;
416
417 got = scm_apply (sharp,
418 SCM_MAKICHR (c),
419 scm_acons (port, SCM_EOL, SCM_EOL));
420 if (SCM_UNSPECIFIED == got)
421 goto unkshrp;
422 if (SCM_RECORD_POSITIONS_P)
423 return *copy = recsexpr (got, line, column,
424 SCM_FILENAME (port));
425 else
426 return got;
427 }
428 }
429 unkshrp:
430 scm_misc_error (s_read, "Unknown # object: %S",
431 scm_listify (SCM_MAKICHR (c), SCM_UNDEFINED));
432 }
433
434 case '"':
435 j = 0;
436 while ('"' != (c = scm_getc (port)))
437 {
438 SCM_ASSERT (EOF != c, SCM_UNDEFINED, "end of file in ", "string");
439
440 while (j + 2 >= SCM_LENGTH (*tok_buf))
441 scm_grow_tok_buf (tok_buf);
442
443 if (c == '\\')
444 switch (c = scm_getc (port))
445 {
446 case '\n':
447 continue;
448 case '0':
449 c = '\0';
450 break;
451 case 'f':
452 c = '\f';
453 break;
454 case 'n':
455 c = '\n';
456 break;
457 case 'r':
458 c = '\r';
459 break;
460 case 't':
461 c = '\t';
462 break;
463 case 'a':
464 c = '\007';
465 break;
466 case 'v':
467 c = '\v';
468 break;
469 }
470 SCM_CHARS (*tok_buf)[j] = c;
471 ++j;
472 }
473 if (j == 0)
474 return scm_nullstr;
475 SCM_CHARS (*tok_buf)[j] = 0;
476 {
477 SCM str;
478 str = scm_makfromstr (SCM_CHARS (*tok_buf), j, 0);
479 return str;
480 }
481
482 case'0':case '1':case '2':case '3':case '4':
483 case '5':case '6':case '7':case '8':case '9':
484 case '.':
485 case '-':
486 case '+':
487 num:
488 j = scm_read_token (c, tok_buf, port, 0);
489 p = scm_istring2number (SCM_CHARS (*tok_buf), (long) j, 10L);
490 if (SCM_NFALSEP (p))
491 return p;
492 if (c == '#')
493 {
494 if ((j == 2) && (scm_getc (port) == '('))
495 {
496 scm_ungetc ('(', port);
497 c = SCM_CHARS (*tok_buf)[1];
498 goto callshrp;
499 }
500 scm_wta (SCM_UNDEFINED, "unknown # object", SCM_CHARS (*tok_buf));
501 }
502 goto tok;
503
504 case ':':
505 if (SCM_KEYWORD_STYLE == scm_keyword_prefix)
506 {
507 j = scm_read_token ('-', tok_buf, port, 0);
508 p = scm_intern (SCM_CHARS (*tok_buf), j);
509 return scm_make_keyword_from_dash_symbol (SCM_CAR (p));
510 }
511 /* fallthrough */
512 default:
513 j = scm_read_token (c, tok_buf, port, 0);
514 /* fallthrough */
515
516 tok:
517 p = scm_intern (SCM_CHARS (*tok_buf), j);
518 return SCM_CAR (p);
519 }
520 }
521
522 #ifdef _UNICOS
523 _Pragma ("noopt"); /* # pragma _CRI noopt */
524 #endif
525
526 scm_sizet
527 scm_read_token (ic, tok_buf, port, weird)
528 int ic;
529 SCM *tok_buf;
530 SCM port;
531 int weird;
532 {
533 register scm_sizet j;
534 register int c;
535 register char *p;
536
537 c = (SCM_CASE_INSENSITIVE_P ? scm_downcase(ic) : ic);
538 p = SCM_CHARS (*tok_buf);
539
540 if (weird)
541 j = 0;
542 else
543 {
544 j = 0;
545 while (j + 2 >= SCM_LENGTH (*tok_buf))
546 p = scm_grow_tok_buf (tok_buf);
547 p[j] = c;
548 ++j;
549 }
550
551 while (1)
552 {
553 while (j + 2 >= SCM_LENGTH (*tok_buf))
554 p = scm_grow_tok_buf (tok_buf);
555 c = scm_getc (port);
556 switch (c)
557 {
558 case '(':
559 case ')':
560 case '"':
561 case ';':
562 case SCM_WHITE_SPACES:
563 case SCM_LINE_INCREMENTORS:
564 if (weird)
565 goto default_case;
566
567 scm_ungetc (c, port);
568 case EOF:
569 eof_case:
570 p[j] = 0;
571 return j;
572 case '\\':
573 if (!weird)
574 goto default_case;
575 else
576 {
577 c = scm_getc (port);
578 if (c == EOF)
579 goto eof_case;
580 else
581 goto default_case;
582 }
583 case '}':
584 if (!weird)
585 goto default_case;
586
587 c = scm_getc (port);
588 if (c == '#')
589 {
590 p[j] = 0;
591 return j;
592 }
593 else
594 {
595 scm_ungetc (c, port);
596 c = '}';
597 goto default_case;
598 }
599
600 default:
601 default_case:
602 {
603 c = (SCM_CASE_INSENSITIVE_P ? scm_downcase(c) : c);
604 p[j] = c;
605 ++j;
606 }
607
608 }
609 }
610 }
611
612 #ifdef _UNICOS
613 _Pragma ("opt"); /* # pragma _CRI opt */
614 #endif
615
616 SCM
617 scm_lreadparen (tok_buf, port, name, copy)
618 SCM *tok_buf;
619 SCM port;
620 char *name;
621 SCM *copy;
622 {
623 SCM tmp;
624 SCM tl;
625 SCM ans;
626 int c;
627
628 c = scm_flush_ws (port, name);
629 if (')' == c)
630 return SCM_EOL;
631 scm_ungetc (c, port);
632 if (scm_i_dot == (tmp = scm_lreadr (tok_buf, port, copy)))
633 {
634 ans = scm_lreadr (tok_buf, port, copy);
635 closeit:
636 if (')' != (c = scm_flush_ws (port, name)))
637 scm_wta (SCM_UNDEFINED, "missing close paren", "");
638 return ans;
639 }
640 ans = tl = scm_cons (tmp, SCM_EOL);
641 while (')' != (c = scm_flush_ws (port, name)))
642 {
643 scm_ungetc (c, port);
644 if (scm_i_dot == (tmp = scm_lreadr (tok_buf, port, copy)))
645 {
646 SCM_SETCDR (tl, scm_lreadr (tok_buf, port, copy));
647 goto closeit;
648 }
649 SCM_SETCDR (tl, scm_cons (tmp, SCM_EOL));
650 tl = SCM_CDR (tl);
651 }
652 return ans;
653 }
654
655
656 SCM
657 scm_lreadrecparen (tok_buf, port, name, copy)
658 SCM *tok_buf;
659 SCM port;
660 char *name;
661 SCM *copy;
662 {
663 register int c;
664 register SCM tmp;
665 register SCM tl, tl2 = SCM_EOL;
666 SCM ans, ans2 = SCM_EOL;
667 /* Need to capture line and column numbers here. */
668 int line = SCM_LINUM (port);
669 int column = SCM_COL (port) - 1;
670
671 c = scm_flush_ws (port, name);
672 if (')' == c)
673 return SCM_EOL;
674 scm_ungetc (c, port);
675 if (scm_i_dot == (tmp = scm_lreadr (tok_buf, port, copy)))
676 {
677 ans = scm_lreadr (tok_buf, port, copy);
678 if (')' != (c = scm_flush_ws (port, name)))
679 scm_wta (SCM_UNDEFINED, "missing close paren", "");
680 return ans;
681 }
682 /* Build the head of the list structure. */
683 ans = tl = scm_cons (tmp, SCM_EOL);
684 if (SCM_COPY_SOURCE_P)
685 ans2 = tl2 = scm_cons (SCM_NIMP (tmp) && SCM_CONSP (tmp)
686 ? *copy
687 : tmp,
688 SCM_EOL);
689 while (')' != (c = scm_flush_ws (port, name)))
690 {
691 scm_ungetc (c, port);
692 if (scm_i_dot == (tmp = scm_lreadr (tok_buf, port, copy)))
693 {
694 SCM_SETCDR (tl, tmp = scm_lreadr (tok_buf, port, copy));
695 if (SCM_COPY_SOURCE_P)
696 SCM_SETCDR (tl2, scm_cons (SCM_NIMP (tmp) && SCM_CONSP (tmp)
697 ? *copy
698 : tmp,
699 SCM_EOL));
700 if (')' != (c = scm_flush_ws (port, name)))
701 scm_wta (SCM_UNDEFINED, "missing close paren", "");
702 goto exit;
703 }
704 tl = SCM_SETCDR (tl, scm_cons (tmp, SCM_EOL));
705 if (SCM_COPY_SOURCE_P)
706 tl2 = SCM_SETCDR (tl2, scm_cons (SCM_NIMP (tmp) && SCM_CONSP (tmp)
707 ? *copy
708 : tmp,
709 SCM_EOL));
710 }
711 exit:
712 scm_whash_insert (scm_source_whash,
713 ans,
714 scm_make_srcprops (line,
715 column,
716 SCM_FILENAME (port),
717 SCM_COPY_SOURCE_P
718 ? *copy = ans2
719 : SCM_UNDEFINED,
720 SCM_EOL));
721 return ans;
722 }
723
724
725 \f
726
727 /* Manipulate the read-hash-procedures alist. This could be written in
728 Scheme, but maybe it will also be used by C code during initialisation. */
729 SCM_PROC (s_read_hash_extend, "read-hash-extend", 2, 0, 0, scm_read_hash_extend);
730 SCM
731 scm_read_hash_extend (chr, proc)
732 SCM chr;
733 SCM proc;
734 {
735 SCM this;
736 SCM prev;
737
738 SCM_ASSERT (SCM_ICHRP(chr), chr, SCM_ARG1, s_read_hash_extend);
739 SCM_ASSERT (SCM_FALSEP (proc) || SCM_NIMP(proc), proc, SCM_ARG2,
740 s_read_hash_extend);
741
742 /* Check if chr is already in the alist. */
743 this = *scm_read_hash_procedures;
744 prev = SCM_BOOL_F;
745 while (1)
746 {
747 if (SCM_NULLP (this))
748 {
749 /* not found, so add it to the beginning. */
750 if (SCM_NFALSEP (proc))
751 {
752 *scm_read_hash_procedures =
753 scm_cons (scm_cons (chr, proc), *scm_read_hash_procedures);
754 }
755 break;
756 }
757 if (chr == SCM_CAAR (this))
758 {
759 /* already in the alist. */
760 if (SCM_FALSEP (proc))
761 {
762 /* remove it. */
763 if (prev == SCM_BOOL_F)
764 {
765 *scm_read_hash_procedures =
766 SCM_CDR (*scm_read_hash_procedures);
767 }
768 else
769 scm_set_cdr_x (prev, SCM_CDR (this));
770 }
771 else
772 {
773 /* replace it. */
774 scm_set_cdr_x (SCM_CAR (this), proc);
775 }
776 break;
777 }
778 prev = this;
779 this = SCM_CDR (this);
780 }
781
782 return SCM_UNSPECIFIED;
783 }
784
785 /* Recover the read-hash procedure corresponding to char c. */
786 static SCM
787 scm_get_hash_procedure (c)
788 int c;
789 {
790 SCM rest = *scm_read_hash_procedures;
791
792 while (1)
793 {
794 if (SCM_NULLP (rest))
795 return SCM_BOOL_F;
796
797 if (SCM_ICHR (SCM_CAAR (rest)) == c)
798 return SCM_CDAR (rest);
799
800 rest = SCM_CDR (rest);
801 }
802 }
803
804 void
805 scm_init_read ()
806 {
807 scm_read_hash_procedures =
808 SCM_CDRLOC (scm_sysintern ("read-hash-procedures", SCM_EOL));
809
810 scm_init_opts (scm_read_options, scm_read_opts, SCM_N_READ_OPTIONS);
811 #include "read.x"
812 }