altos/scheme: Add ports. Split scheme code up.
[fw/altos] / src / scheme / ao_scheme_read.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include "ao_scheme.h"
16 #include "ao_scheme_read.h"
17 #include <math.h>
18 #include <stdlib.h>
19
20 static const uint16_t   lex_classes[128] = {
21         IGNORE,                         /* ^@ */
22         IGNORE,                         /* ^A */
23         IGNORE,                         /* ^B */
24         IGNORE,                         /* ^C */
25         IGNORE,                         /* ^D */
26         IGNORE,                         /* ^E */
27         IGNORE,                         /* ^F */
28         IGNORE,                         /* ^G */
29         IGNORE,                         /* ^H */
30         WHITE,                          /* ^I */
31         WHITE,                          /* ^J */
32         WHITE,                          /* ^K */
33         WHITE,                          /* ^L */
34         WHITE,                          /* ^M */
35         IGNORE,                         /* ^N */
36         IGNORE,                         /* ^O */
37         IGNORE,                         /* ^P */
38         IGNORE,                         /* ^Q */
39         IGNORE,                         /* ^R */
40         IGNORE,                         /* ^S */
41         IGNORE,                         /* ^T */
42         IGNORE,                         /* ^U */
43         IGNORE,                         /* ^V */
44         IGNORE,                         /* ^W */
45         IGNORE,                         /* ^X */
46         IGNORE,                         /* ^Y */
47         IGNORE,                         /* ^Z */
48         IGNORE,                         /* ^[ */
49         IGNORE,                         /* ^\ */
50         IGNORE,                         /* ^] */
51         IGNORE,                         /* ^^ */
52         IGNORE,                         /* ^_ */
53         PRINTABLE|WHITE,                /*    */
54         PRINTABLE,                      /* ! */
55         PRINTABLE|STRINGC,              /* " */
56         PRINTABLE,                      /* # */
57         PRINTABLE,                      /* $ */
58         PRINTABLE,                      /* % */
59         PRINTABLE,                      /* & */
60         PRINTABLE|SPECIAL,              /* ' */
61         PRINTABLE|SPECIAL,              /* ( */
62         PRINTABLE|SPECIAL,              /* ) */
63         PRINTABLE,                      /* * */
64         PRINTABLE|SIGN,                 /* + */
65         PRINTABLE|SPECIAL_QUASI,        /* , */
66         PRINTABLE|SIGN,                 /* - */
67         PRINTABLE|SPECIAL|FLOATC,       /* . */
68         PRINTABLE,                      /* / */
69         PRINTABLE|DIGIT,                /* 0 */
70         PRINTABLE|DIGIT,                /* 1 */
71         PRINTABLE|DIGIT,                /* 2 */
72         PRINTABLE|DIGIT,                /* 3 */
73         PRINTABLE|DIGIT,                /* 4 */
74         PRINTABLE|DIGIT,                /* 5 */
75         PRINTABLE|DIGIT,                /* 6 */
76         PRINTABLE|DIGIT,                /* 7 */
77         PRINTABLE|DIGIT,                /* 8 */
78         PRINTABLE|DIGIT,                /* 9 */
79         PRINTABLE,                      /* : */
80         PRINTABLE|COMMENT,              /* ; */
81         PRINTABLE,                      /* < */
82         PRINTABLE,                      /* = */
83         PRINTABLE,                      /* > */
84         PRINTABLE,                      /* ? */
85         PRINTABLE,                      /*  @ */
86         PRINTABLE|ALPHA|HEX_LETTER,     /*  A */
87         PRINTABLE|ALPHA|HEX_LETTER,     /*  B */
88         PRINTABLE|ALPHA|HEX_LETTER,     /*  C */
89         PRINTABLE|ALPHA|HEX_LETTER,     /*  D */
90         PRINTABLE|ALPHA|FLOATC|HEX_LETTER,/*  E */
91         PRINTABLE|ALPHA|HEX_LETTER,     /*  F */
92         PRINTABLE|ALPHA,                /*  G */
93         PRINTABLE|ALPHA,                /*  H */
94         PRINTABLE|ALPHA,                /*  I */
95         PRINTABLE|ALPHA,                /*  J */
96         PRINTABLE|ALPHA,                /*  K */
97         PRINTABLE|ALPHA,                /*  L */
98         PRINTABLE|ALPHA,                /*  M */
99         PRINTABLE|ALPHA,                /*  N */
100         PRINTABLE|ALPHA,                /*  O */
101         PRINTABLE|ALPHA,                /*  P */
102         PRINTABLE|ALPHA,                /*  Q */
103         PRINTABLE|ALPHA,                /*  R */
104         PRINTABLE|ALPHA,                /*  S */
105         PRINTABLE|ALPHA,                /*  T */
106         PRINTABLE|ALPHA,                /*  U */
107         PRINTABLE|ALPHA,                /*  V */
108         PRINTABLE|ALPHA,                /*  W */
109         PRINTABLE|ALPHA,                /*  X */
110         PRINTABLE|ALPHA,                /*  Y */
111         PRINTABLE|ALPHA,                /*  Z */
112         PRINTABLE,                      /*  [ */
113         PRINTABLE,                      /*  \ */
114         PRINTABLE,                      /*  ] */
115         PRINTABLE,                      /*  ^ */
116         PRINTABLE,                      /*  _ */
117         PRINTABLE|SPECIAL_QUASI,        /*  ` */
118         PRINTABLE|ALPHA|HEX_LETTER,     /*  a */
119         PRINTABLE|ALPHA|HEX_LETTER,     /*  b */
120         PRINTABLE|ALPHA|HEX_LETTER,     /*  c */
121         PRINTABLE|ALPHA|HEX_LETTER,     /*  d */
122         PRINTABLE|ALPHA|FLOATC|HEX_LETTER,/*  e */
123         PRINTABLE|ALPHA|HEX_LETTER,     /*  f */
124         PRINTABLE|ALPHA,                /*  g */
125         PRINTABLE|ALPHA,                /*  h */
126         PRINTABLE|ALPHA,                /*  i */
127         PRINTABLE|ALPHA,                /*  j */
128         PRINTABLE|ALPHA,                /*  k */
129         PRINTABLE|ALPHA,                /*  l */
130         PRINTABLE|ALPHA,                /*  m */
131         PRINTABLE|ALPHA,                /*  n */
132         PRINTABLE|ALPHA,                /*  o */
133         PRINTABLE|ALPHA,                /*  p */
134         PRINTABLE|ALPHA,                /*  q */
135         PRINTABLE|ALPHA,                /*  r */
136         PRINTABLE|ALPHA,                /*  s */
137         PRINTABLE|ALPHA,                /*  t */
138         PRINTABLE|ALPHA,                /*  u */
139         PRINTABLE|ALPHA,                /*  v */
140         PRINTABLE|ALPHA,                /*  w */
141         PRINTABLE|ALPHA,                /*  x */
142         PRINTABLE|ALPHA,                /*  y */
143         PRINTABLE|ALPHA,                /*  z */
144         PRINTABLE,                      /*  { */
145         PRINTABLE,                      /*  | */
146         PRINTABLE,                      /*  } */
147         PRINTABLE,                      /*  ~ */
148         IGNORE,                         /*  ^? */
149 };
150
151 static int lex_unget_c;
152
153 static inline int
154 lex_get(FILE *in)
155 {
156         int     c;
157         if (lex_unget_c) {
158                 c = lex_unget_c;
159                 lex_unget_c = 0;
160         } else {
161                 c = getc(in);
162         }
163         return c;
164 }
165
166 static inline void
167 lex_unget(int c)
168 {
169         if (c != EOF)
170                 lex_unget_c = c;
171 }
172
173 static uint16_t lex_class;
174
175 static int
176 lexc(FILE *in)
177 {
178         int     c;
179         do {
180                 c = lex_get(in);
181                 if (c == EOF) {
182                         c = 0;
183                         lex_class = ENDOFFILE;
184                 } else {
185                         c &= 0x7f;
186                         lex_class = lex_classes[c];
187                 }
188         } while (lex_class & IGNORE);
189         return c;
190 }
191
192 static int
193 lex_quoted(FILE *in)
194 {
195         int     c;
196         int     v;
197         int     count;
198
199         c = lex_get(in);
200         if (c == EOF) {
201         eof:
202                 lex_class = ENDOFFILE;
203                 return 0;
204         }
205         lex_class = 0;
206         c &= 0x7f;
207         switch (c) {
208         case 'a':
209                 return '\a';
210         case 'b':
211                 return '\b';
212         case 't':
213                 return '\t';
214         case 'n':
215                 return '\n';
216         case 'r':
217                 return '\r';
218         case 'f':
219                 return '\f';
220         case 'v':
221                 return '\v';
222         case '0':
223         case '1':
224         case '2':
225         case '3':
226         case '4':
227         case '5':
228         case '6':
229         case '7':
230                 v = c - '0';
231                 count = 1;
232                 while (count <= 3) {
233                         c = lex_get(in);
234                         if (c == EOF)
235                                 goto eof;
236                         c &= 0x7f;
237                         if (c < '0' || '7' < c) {
238                                 lex_unget(c);
239                                 break;
240                         }
241                         v = (v << 3) + c - '0';
242                         ++count;
243                 }
244                 return v;
245         default:
246                 return c;
247         }
248 }
249
250 #ifndef AO_SCHEME_TOKEN_MAX
251 #define AO_SCHEME_TOKEN_MAX     128
252 #endif
253
254 static char     token_string[AO_SCHEME_TOKEN_MAX];
255 static int32_t  token_int;
256 static int      token_len;
257
258 static void start_token(void) {
259         token_len = 0;
260 }
261
262 static void add_token(int c) {
263         if (c && token_len < AO_SCHEME_TOKEN_MAX - 1)
264                 token_string[token_len++] = c;
265 }
266
267 static void end_token(void) {
268         token_string[token_len] = '\0';
269 }
270
271 #ifdef AO_SCHEME_FEATURE_FLOAT
272 static float    token_float;
273
274 struct namedfloat {
275         const char      *name;
276         float           value;
277 };
278
279 static const struct namedfloat namedfloats[] = {
280         { .name = "+inf.0", .value = INFINITY },
281         { .name = "-inf.0", .value = -INFINITY },
282         { .name = "+nan.0", .value = NAN },
283         { .name = "-nan.0", .value = NAN },
284 };
285
286 #define NUM_NAMED_FLOATS        (sizeof namedfloats / sizeof namedfloats[0])
287 #endif
288
289 static int
290 parse_int(FILE *in, int base)
291 {
292         int     cval;
293         int     c;
294
295         token_int = 0;
296         for (;;) {
297                 c = lexc(in);
298                 if ((lex_class & HEX_DIGIT) == 0) {
299                         lex_unget(c);
300                         return NUM;
301                 }
302                 if ('0' <= c && c <= '9')
303                         cval = c - '0';
304                 else
305                         cval = (c | ('a' - 'A')) - 'a' + 10;
306                 token_int = token_int * base + cval;
307         }
308         return NUM;
309 }
310
311 static int
312 _lex(FILE *in)
313 {
314         int     c;
315
316         start_token();
317         for (;;) {
318                 c = lexc(in);
319                 if (lex_class & ENDOFFILE)
320                         return END;
321
322                 if (lex_class & WHITE)
323                         continue;
324
325                 if (lex_class & COMMENT) {
326                         while ((c = lexc(in)) != '\n') {
327                                 if (lex_class & ENDOFFILE)
328                                         return END;
329                         }
330                         continue;
331                 }
332
333                 if (lex_class & SPECIAL) {
334                         switch (c) {
335                         case '(':
336                         case '[':
337                                 return OPEN;
338                         case ')':
339                         case ']':
340                                 return CLOSE;
341                         case '\'':
342                                 return QUOTE;
343                         case '.':
344                                 return DOT;
345 #ifdef AO_SCHEME_FEATURE_QUASI
346                         case '`':
347                                 return QUASIQUOTE;
348                         case ',':
349                                 c = lexc(in);
350                                 if (c == '@') {
351                                         return UNQUOTE_SPLICING;
352                                 } else {
353                                         lex_unget(c);
354                                         return UNQUOTE;
355                                 }
356 #endif
357                         }
358                 }
359                 if (c == '#') {
360                         c = lexc(in);
361                         switch (c) {
362                         case 't':
363                                 return TRUE_TOKEN;
364                         case 'f':
365                                 return FALSE_TOKEN;
366 #ifdef AO_SCHEME_FEATURE_VECTOR
367                         case '(':
368                                 return OPEN_VECTOR;
369 #endif
370                         case '\\':
371                                 for (;;) {
372                                         c = lexc(in);
373                                         if (token_len == 0) {
374                                                 add_token(c);
375                                                 if (!(lex_class & ALPHA))
376                                                         break;
377                                         } else {
378                                                 if (lex_class & ALPHA)
379                                                         add_token(c);
380                                                 else {
381                                                         lex_unget(c);
382                                                         break;
383                                                 }
384                                         }
385                                 }
386                                 end_token();
387                                 if (token_len == 1)
388                                         token_int = token_string[0];
389                                 else if (!strcmp(token_string, "space"))
390                                         token_int = ' ';
391                                 else if (!strcmp(token_string, "newline"))
392                                         token_int = '\n';
393                                 else if (!strcmp(token_string, "tab"))
394                                         token_int = '\t';
395                                 else if (!strcmp(token_string, "return"))
396                                         token_int = '\r';
397                                 else if (!strcmp(token_string, "formfeed"))
398                                         token_int = '\f';
399                                 else {
400                                         ao_scheme_error(AO_SCHEME_INVALID, "invalid character token #\\%s", token_string);
401                                         continue;
402                                 }
403                                 return NUM;
404                         case 'x':
405                                 return parse_int(in, 16);
406                         case 'o':
407                                 return parse_int(in, 8);
408                         case 'b':
409                                 return parse_int(in, 2);
410                         }
411                 }
412                 if (lex_class & STRINGC) {
413                         for (;;) {
414                                 c = lexc(in);
415                                 if (c == '\\')
416                                         c = lex_quoted(in);
417                                 if (lex_class & (STRINGC|ENDOFFILE)) {
418                                         end_token();
419                                         return STRING;
420                                 }
421                                 add_token(c);
422                         }
423                 }
424                 if (lex_class & PRINTABLE) {
425 #ifdef AO_SCHEME_FEATURE_FLOAT
426                         int     isfloat = 1;
427                         int     epos = 0;
428 #endif
429                         int     hasdigit = 0;
430                         int     isneg = 0;
431                         int     isint = 1;
432
433                         token_int = 0;
434                         for (;;) {
435                                 if (!(lex_class & NUMBER)) {
436                                         isint = 0;
437 #ifdef AO_SCHEME_FEATURE_FLOAT
438                                         isfloat = 0;
439 #endif
440                                 } else {
441 #ifdef AO_SCHEME_FEATURE_FLOAT
442                                         if (!(lex_class & INTEGER))
443                                                 isint = 0;
444                                         if (token_len != epos &&
445                                             (lex_class & SIGN))
446                                         {
447                                                 isint = 0;
448                                                 isfloat = 0;
449                                         }
450 #endif
451                                         if (c == '-')
452                                                 isneg = 1;
453 #ifdef AO_SCHEME_FEATURE_FLOAT
454                                         if (c == '.' && epos != 0)
455                                                 isfloat = 0;
456                                         if (c == 'e' || c == 'E') {
457                                                 if (token_len == 0)
458                                                         isfloat = 0;
459                                                 else
460                                                         epos = token_len + 1;
461                                         }
462 #endif
463                                         if (lex_class & DIGIT) {
464                                                 hasdigit = 1;
465                                                 if (isint)
466                                                         token_int = token_int * 10 + c - '0';
467                                         }
468                                 }
469                                 add_token (c);
470                                 c = lexc (in);
471                                 if ((lex_class & (NOTNAME))
472 #ifdef AO_SCHEME_FEATURE_FLOAT
473                                     && (c != '.' || !isfloat)
474 #endif
475                                         ) {
476 #ifdef AO_SCHEME_FEATURE_FLOAT
477                                         unsigned int u;
478 #endif
479                                         lex_unget(c);
480                                         end_token ();
481                                         if (isint && hasdigit) {
482                                                 if (isneg)
483                                                         token_int = -token_int;
484                                                 return NUM;
485                                         }
486 #ifdef AO_SCHEME_FEATURE_FLOAT
487                                         if (isfloat && hasdigit) {
488                                                 token_float = strtof(token_string, NULL);
489                                                 return FLOAT;
490                                         }
491                                         for (u = 0; u < NUM_NAMED_FLOATS; u++)
492                                                 if (!strcmp(namedfloats[u].name, token_string)) {
493                                                         token_float = namedfloats[u].value;
494                                                         return FLOAT;
495                                                 }
496 #endif
497                                         return NAME;
498                                 }
499                         }
500                 }
501         }
502 }
503
504 static inline int lex(FILE *in)
505 {
506         int     parse_token = _lex(in);
507         RDBGI("token %d \"%s\"\n", parse_token, token_string);
508         return parse_token;
509 }
510
511 static int parse_token;
512
513 int                     ao_scheme_read_list;
514 struct ao_scheme_cons   *ao_scheme_read_cons;
515 struct ao_scheme_cons   *ao_scheme_read_cons_tail;
516 struct ao_scheme_cons   *ao_scheme_read_stack;
517 static int              ao_scheme_read_state;
518
519 #define READ_IN_QUOTE   0x01
520 #define READ_SAW_DOT    0x02
521 #define READ_DONE_DOT   0x04
522 #define READ_SAW_VECTOR 0x08
523
524 static int
525 push_read_stack(int read_state)
526 {
527         RDBGI("push read stack %p 0x%x\n", ao_scheme_read_cons, read_state);
528         RDBG_IN();
529         if (ao_scheme_read_list) {
530                 ao_scheme_read_stack = ao_scheme_cons_cons(ao_scheme_cons_poly(ao_scheme_read_cons),
531                                                        ao_scheme_cons(ao_scheme_int_poly(read_state),
532                                                                      ao_scheme_cons_poly(ao_scheme_read_stack)));
533                 if (!ao_scheme_read_stack)
534                         return 0;
535         } else
536                 ao_scheme_read_state = read_state;
537         ao_scheme_read_cons = NULL;
538         ao_scheme_read_cons_tail = NULL;
539         return 1;
540 }
541
542 static int
543 pop_read_stack(void)
544 {
545         int     read_state = 0;
546         if (ao_scheme_read_list) {
547                 ao_scheme_read_cons = ao_scheme_poly_cons(ao_scheme_read_stack->car);
548                 ao_scheme_read_stack = ao_scheme_poly_cons(ao_scheme_read_stack->cdr);
549                 read_state = ao_scheme_poly_int(ao_scheme_read_stack->car);
550                 ao_scheme_read_stack = ao_scheme_poly_cons(ao_scheme_read_stack->cdr);
551                 for (ao_scheme_read_cons_tail = ao_scheme_read_cons;
552                      ao_scheme_read_cons_tail && ao_scheme_read_cons_tail->cdr;
553                      ao_scheme_read_cons_tail = ao_scheme_poly_cons(ao_scheme_read_cons_tail->cdr))
554                         ;
555         } else {
556                 read_state = ao_scheme_read_state;
557                 ao_scheme_read_cons = NULL;
558                 ao_scheme_read_cons_tail = NULL;
559                 ao_scheme_read_stack = NULL;
560                 ao_scheme_read_state = 0;
561         }
562         RDBG_OUT();
563         RDBGI("pop read stack %p %d\n", ao_scheme_read_cons, read_state);
564         return read_state;
565 }
566
567 #ifdef AO_SCHEME_FEATURE_VECTOR
568 #define is_open(t) ((t) == OPEN || (t) == OPEN_VECTOR)
569 #else
570 #define is_open(t) ((t) == OPEN)
571 #endif
572
573 ao_poly
574 ao_scheme_read(FILE *in)
575 {
576         struct ao_scheme_atom   *atom;
577         struct ao_scheme_string *string;
578         int                     read_state;
579         ao_poly                 v = AO_SCHEME_NIL;
580
581         ao_scheme_read_list = 0;
582         read_state = 0;
583         ao_scheme_read_cons = ao_scheme_read_cons_tail = ao_scheme_read_stack = NULL;
584         for (;;) {
585                 parse_token = lex(in);
586                 while (is_open(parse_token)) {
587 #ifdef AO_SCHEME_FEATURE_VECTOR
588                         if (parse_token == OPEN_VECTOR)
589                                 read_state |= READ_SAW_VECTOR;
590 #endif
591                         if (!push_read_stack(read_state))
592                                 return AO_SCHEME_NIL;
593                         ao_scheme_read_list++;
594                         read_state = 0;
595                         parse_token = lex(in);
596                 }
597
598                 switch (parse_token) {
599                 case END:
600                 default:
601                         if (ao_scheme_read_list)
602                                 ao_scheme_error(AO_SCHEME_EOF, "unexpected end of file");
603                         return _ao_scheme_atom_eof;
604                         break;
605                 case NAME:
606                         atom = ao_scheme_atom_intern(token_string);
607                         if (atom)
608                                 v = ao_scheme_atom_poly(atom);
609                         else
610                                 v = AO_SCHEME_NIL;
611                         break;
612                 case NUM:
613                         v = ao_scheme_integer_poly(token_int);
614                         break;
615 #ifdef AO_SCHEME_FEATURE_FLOAT
616                 case FLOAT:
617                         v = ao_scheme_float_get(token_float);
618                         break;
619 #endif
620                 case TRUE_TOKEN:
621                         v = _ao_scheme_bool_true;
622                         break;
623                 case FALSE_TOKEN:
624                         v = _ao_scheme_bool_false;
625                         break;
626                 case STRING:
627                         string = ao_scheme_string_new(token_string);
628                         if (string)
629                                 v = ao_scheme_string_poly(string);
630                         else
631                                 v = AO_SCHEME_NIL;
632                         break;
633                 case QUOTE:
634 #ifdef AO_SCHEME_FEATURE_QUASI
635                 case QUASIQUOTE:
636                 case UNQUOTE:
637                 case UNQUOTE_SPLICING:
638 #endif
639                         if (!push_read_stack(read_state))
640                                 return AO_SCHEME_NIL;
641                         ao_scheme_read_list++;
642                         read_state = READ_IN_QUOTE;
643                         switch (parse_token) {
644                         case QUOTE:
645                                 v = _ao_scheme_atom_quote;
646                                 break;
647 #ifdef AO_SCHEME_FEATURE_QUASI
648                         case QUASIQUOTE:
649                                 v = _ao_scheme_atom_quasiquote;
650                                 break;
651                         case UNQUOTE:
652                                 v = _ao_scheme_atom_unquote;
653                                 break;
654                         case UNQUOTE_SPLICING:
655                                 v = _ao_scheme_atom_unquote2dsplicing;
656                                 break;
657 #endif
658                         }
659                         break;
660                 case CLOSE:
661                         if (!ao_scheme_read_list) {
662                                 v = AO_SCHEME_NIL;
663                                 break;
664                         }
665                         v = ao_scheme_cons_poly(ao_scheme_read_cons);
666                         --ao_scheme_read_list;
667                         read_state = pop_read_stack();
668 #ifdef AO_SCHEME_FEATURE_VECTOR
669                         if (read_state & READ_SAW_VECTOR) {
670                                 v = ao_scheme_vector_poly(ao_scheme_list_to_vector(ao_scheme_poly_cons(v)));
671                                 read_state &= ~READ_SAW_VECTOR;
672                         }
673 #endif
674                         break;
675                 case DOT:
676                         if (!ao_scheme_read_list) {
677                                 ao_scheme_error(AO_SCHEME_INVALID, ". outside of cons");
678                                 return AO_SCHEME_NIL;
679                         }
680                         if (!ao_scheme_read_cons) {
681                                 ao_scheme_error(AO_SCHEME_INVALID, ". first in cons");
682                                 return AO_SCHEME_NIL;
683                         }
684                         read_state |= READ_SAW_DOT;
685                         continue;
686                 }
687
688                 /* loop over QUOTE ends */
689                 for (;;) {
690                         if (!ao_scheme_read_list)
691                                 return v;
692
693                         if (read_state & READ_DONE_DOT) {
694                                 ao_scheme_error(AO_SCHEME_INVALID, ". not last in cons");
695                                 return AO_SCHEME_NIL;
696                         }
697
698                         if (read_state & READ_SAW_DOT) {
699                                 read_state |= READ_DONE_DOT;
700                                 ao_scheme_read_cons_tail->cdr = v;
701                         } else {
702                                 struct ao_scheme_cons   *read = ao_scheme_cons_cons(v, AO_SCHEME_NIL);
703                                 if (!read)
704                                         return AO_SCHEME_NIL;
705
706                                 if (ao_scheme_read_cons_tail)
707                                         ao_scheme_read_cons_tail->cdr = ao_scheme_cons_poly(read);
708                                 else
709                                         ao_scheme_read_cons = read;
710                                 ao_scheme_read_cons_tail = read;
711                         }
712
713                         if (!(read_state & READ_IN_QUOTE) || !ao_scheme_read_cons->cdr)
714                                 break;
715
716                         v = ao_scheme_cons_poly(ao_scheme_read_cons);
717                         --ao_scheme_read_list;
718                         read_state = pop_read_stack();
719                 }
720         }
721         return v;
722 }