3575ff3fff14cfcdaa3ac3afec22a6c960243624
[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 #ifndef ao_scheme_getc
154 #define ao_scheme_getc(f) getc(f)
155 #endif
156
157 static inline int
158 lex_get(FILE *in)
159 {
160         int     c;
161         if (lex_unget_c) {
162                 c = lex_unget_c;
163                 lex_unget_c = 0;
164         } else {
165                 c = ao_scheme_getc(in);
166         }
167         return c;
168 }
169
170 static inline void
171 lex_unget(int c)
172 {
173         if (c != EOF)
174                 lex_unget_c = c;
175 }
176
177 static uint16_t lex_class;
178
179 static int
180 lexc(FILE *in)
181 {
182         int     c;
183         do {
184                 c = lex_get(in);
185                 if (c == EOF) {
186                         c = 0;
187                         lex_class = ENDOFFILE;
188                 } else {
189                         lex_class = PRINTABLE;
190                         if (c <= 0x7f)
191                                 lex_class = lex_classes[c];
192                 }
193         } while (lex_class & IGNORE);
194         return c;
195 }
196
197 static int
198 lex_quoted(FILE *in)
199 {
200         int     c;
201         int     v;
202         int     count;
203
204         c = lex_get(in);
205         if (c == EOF) {
206         eof:
207                 lex_class = ENDOFFILE;
208                 return 0;
209         }
210         lex_class = 0;
211         c &= 0x7f;
212         switch (c) {
213         case 'a':
214                 return '\a';
215         case 'b':
216                 return '\b';
217         case 't':
218                 return '\t';
219         case 'n':
220                 return '\n';
221         case 'r':
222                 return '\r';
223         case 'f':
224                 return '\f';
225         case 'v':
226                 return '\v';
227         case '0':
228         case '1':
229         case '2':
230         case '3':
231         case '4':
232         case '5':
233         case '6':
234         case '7':
235                 v = c - '0';
236                 count = 1;
237                 while (count <= 3) {
238                         c = lex_get(in);
239                         if (c == EOF)
240                                 goto eof;
241                         c &= 0x7f;
242                         if (c < '0' || '7' < c) {
243                                 lex_unget(c);
244                                 break;
245                         }
246                         v = (v << 3) + c - '0';
247                         ++count;
248                 }
249                 return v;
250         default:
251                 return c;
252         }
253 }
254
255 #ifndef AO_SCHEME_TOKEN_MAX
256 #define AO_SCHEME_TOKEN_MAX     128
257 #endif
258
259 static char     token_string[AO_SCHEME_TOKEN_MAX];
260 static int32_t  token_int;
261 static int      token_len;
262
263 static void start_token(void) {
264         token_len = 0;
265 }
266
267 static void add_token(int c) {
268         if (c && token_len < AO_SCHEME_TOKEN_MAX - 1)
269                 token_string[token_len++] = c;
270 }
271
272 static void end_token(void) {
273         token_string[token_len] = '\0';
274 }
275
276 #ifdef AO_SCHEME_FEATURE_FLOAT
277 static float    token_float;
278
279 struct namedfloat {
280         const char      *name;
281         float           value;
282 };
283
284 static const struct namedfloat namedfloats[] = {
285         { .name = "+inf.0", .value = INFINITY },
286         { .name = "-inf.0", .value = -INFINITY },
287         { .name = "+nan.0", .value = NAN },
288         { .name = "-nan.0", .value = NAN },
289 };
290
291 #define NUM_NAMED_FLOATS        (sizeof namedfloats / sizeof namedfloats[0])
292 #endif
293
294 static int
295 parse_int(FILE *in, int base)
296 {
297         int     cval;
298         int     c;
299
300         token_int = 0;
301         for (;;) {
302                 c = lexc(in);
303                 if ((lex_class & HEX_DIGIT) == 0) {
304                         lex_unget(c);
305                         return NUM;
306                 }
307                 if ('0' <= c && c <= '9')
308                         cval = c - '0';
309                 else
310                         cval = (c | ('a' - 'A')) - 'a' + 10;
311                 token_int = token_int * base + cval;
312         }
313         return NUM;
314 }
315
316 static int
317 _lex(FILE *in)
318 {
319         int     c;
320
321         start_token();
322         for (;;) {
323                 c = lexc(in);
324                 if (lex_class & ENDOFFILE)
325                         return END;
326
327                 if (lex_class & WHITE)
328                         continue;
329
330                 if (lex_class & COMMENT) {
331                         while ((c = lexc(in)) != '\n') {
332                                 if (lex_class & ENDOFFILE)
333                                         return END;
334                         }
335                         continue;
336                 }
337
338                 if (lex_class & SPECIAL) {
339                         switch (c) {
340                         case '(':
341                         case '[':
342                                 return OPEN;
343                         case ')':
344                         case ']':
345                                 return CLOSE;
346                         case '\'':
347                                 return QUOTE;
348                         case '.':
349                                 return DOT;
350 #ifdef AO_SCHEME_FEATURE_QUASI
351                         case '`':
352                                 return QUASIQUOTE;
353                         case ',':
354                                 c = lexc(in);
355                                 if (c == '@') {
356                                         return UNQUOTE_SPLICING;
357                                 } else {
358                                         lex_unget(c);
359                                         return UNQUOTE;
360                                 }
361 #endif
362                         }
363                 }
364                 if (c == '#') {
365                         c = lexc(in);
366                         switch (c) {
367                         case 't':
368                                 return TRUE_TOKEN;
369                         case 'f':
370                                 return FALSE_TOKEN;
371 #ifdef AO_SCHEME_FEATURE_VECTOR
372                         case '(':
373                                 return OPEN_VECTOR;
374 #endif
375                         case '\\':
376                                 for (;;) {
377                                         c = lexc(in);
378                                         if (token_len == 0) {
379                                                 add_token(c);
380                                                 if (!(lex_class & ALPHA))
381                                                         break;
382                                         } else {
383                                                 if (lex_class & ALPHA)
384                                                         add_token(c);
385                                                 else {
386                                                         lex_unget(c);
387                                                         break;
388                                                 }
389                                         }
390                                 }
391                                 end_token();
392                                 if (token_len == 1)
393                                         token_int = token_string[0];
394                                 else if (!strcmp(token_string, "space"))
395                                         token_int = ' ';
396                                 else if (!strcmp(token_string, "newline"))
397                                         token_int = '\n';
398                                 else if (!strcmp(token_string, "tab"))
399                                         token_int = '\t';
400                                 else if (!strcmp(token_string, "return"))
401                                         token_int = '\r';
402                                 else if (!strcmp(token_string, "formfeed"))
403                                         token_int = '\f';
404                                 else {
405                                         ao_scheme_error(AO_SCHEME_INVALID, "invalid character token #\\%s", token_string);
406                                         continue;
407                                 }
408                                 return NUM;
409                         case 'x':
410                                 return parse_int(in, 16);
411                         case 'o':
412                                 return parse_int(in, 8);
413                         case 'b':
414                                 return parse_int(in, 2);
415                         }
416                 }
417                 if (lex_class & STRINGC) {
418                         for (;;) {
419                                 c = lexc(in);
420                                 if (c == '\\')
421                                         c = lex_quoted(in);
422                                 if (lex_class & (STRINGC|ENDOFFILE)) {
423                                         end_token();
424                                         return STRING;
425                                 }
426                                 add_token(c);
427                         }
428                 }
429                 if (lex_class & PRINTABLE) {
430 #ifdef AO_SCHEME_FEATURE_FLOAT
431                         int     isfloat = 1;
432                         int     epos = 0;
433 #endif
434                         int     hasdigit = 0;
435                         int     isneg = 0;
436                         int     isint = 1;
437
438                         token_int = 0;
439                         for (;;) {
440                                 if (!(lex_class & NUMBER)) {
441                                         isint = 0;
442 #ifdef AO_SCHEME_FEATURE_FLOAT
443                                         isfloat = 0;
444 #endif
445                                 } else {
446 #ifdef AO_SCHEME_FEATURE_FLOAT
447                                         if (!(lex_class & INTEGER))
448                                                 isint = 0;
449                                         if (token_len != epos &&
450                                             (lex_class & SIGN))
451                                         {
452                                                 isint = 0;
453                                                 isfloat = 0;
454                                         }
455 #endif
456                                         if (c == '-')
457                                                 isneg = 1;
458 #ifdef AO_SCHEME_FEATURE_FLOAT
459                                         if (c == '.' && epos != 0)
460                                                 isfloat = 0;
461                                         if (c == 'e' || c == 'E') {
462                                                 if (token_len == 0)
463                                                         isfloat = 0;
464                                                 else
465                                                         epos = token_len + 1;
466                                         }
467 #endif
468                                         if (lex_class & DIGIT) {
469                                                 hasdigit = 1;
470                                                 if (isint)
471                                                         token_int = token_int * 10 + c - '0';
472                                         }
473                                 }
474                                 add_token (c);
475                                 c = lexc (in);
476                                 if ((lex_class & (NOTNAME))
477 #ifdef AO_SCHEME_FEATURE_FLOAT
478                                     && (c != '.' || !isfloat)
479 #endif
480                                         ) {
481 #ifdef AO_SCHEME_FEATURE_FLOAT
482                                         unsigned int u;
483 #endif
484                                         lex_unget(c);
485                                         end_token ();
486                                         if (isint && hasdigit) {
487                                                 if (isneg)
488                                                         token_int = -token_int;
489                                                 return NUM;
490                                         }
491 #ifdef AO_SCHEME_FEATURE_FLOAT
492                                         if (isfloat && hasdigit) {
493                                                 token_float = strtof(token_string, NULL);
494                                                 return FLOAT;
495                                         }
496                                         for (u = 0; u < NUM_NAMED_FLOATS; u++)
497                                                 if (!strcmp(namedfloats[u].name, token_string)) {
498                                                         token_float = namedfloats[u].value;
499                                                         return FLOAT;
500                                                 }
501 #endif
502                                         return NAME;
503                                 }
504                         }
505                 }
506         }
507 }
508
509 static inline int lex(FILE *in)
510 {
511         int     parse_token = _lex(in);
512         RDBGI("token %d \"%s\"\n", parse_token, token_string);
513         return parse_token;
514 }
515
516 static int parse_token;
517
518 int                     ao_scheme_read_list;
519 struct ao_scheme_cons   *ao_scheme_read_cons;
520 struct ao_scheme_cons   *ao_scheme_read_cons_tail;
521 struct ao_scheme_cons   *ao_scheme_read_stack;
522 static int              ao_scheme_read_state;
523
524 #define READ_IN_QUOTE   0x01
525 #define READ_SAW_DOT    0x02
526 #define READ_DONE_DOT   0x04
527 #define READ_SAW_VECTOR 0x08
528
529 static int
530 push_read_stack(int read_state)
531 {
532         RDBGI("push read stack %p 0x%x\n", ao_scheme_read_cons, read_state);
533         RDBG_IN();
534         if (ao_scheme_read_list) {
535                 ao_scheme_read_stack = ao_scheme_cons_cons(ao_scheme_cons_poly(ao_scheme_read_cons),
536                                                        ao_scheme_cons(ao_scheme_int_poly(read_state),
537                                                                      ao_scheme_cons_poly(ao_scheme_read_stack)));
538                 if (!ao_scheme_read_stack)
539                         return 0;
540         } else
541                 ao_scheme_read_state = read_state;
542         ao_scheme_read_cons = NULL;
543         ao_scheme_read_cons_tail = NULL;
544         return 1;
545 }
546
547 static int
548 pop_read_stack(void)
549 {
550         int     read_state = 0;
551         if (ao_scheme_read_list) {
552                 ao_scheme_read_cons = ao_scheme_poly_cons(ao_scheme_read_stack->car);
553                 ao_scheme_read_stack = ao_scheme_poly_cons(ao_scheme_read_stack->cdr);
554                 read_state = ao_scheme_poly_int(ao_scheme_read_stack->car);
555                 ao_scheme_read_stack = ao_scheme_poly_cons(ao_scheme_read_stack->cdr);
556                 for (ao_scheme_read_cons_tail = ao_scheme_read_cons;
557                      ao_scheme_read_cons_tail && ao_scheme_read_cons_tail->cdr;
558                      ao_scheme_read_cons_tail = ao_scheme_poly_cons(ao_scheme_read_cons_tail->cdr))
559                         ;
560         } else {
561                 read_state = ao_scheme_read_state;
562                 ao_scheme_read_cons = NULL;
563                 ao_scheme_read_cons_tail = NULL;
564                 ao_scheme_read_stack = NULL;
565                 ao_scheme_read_state = 0;
566         }
567         RDBG_OUT();
568         RDBGI("pop read stack %p %d\n", ao_scheme_read_cons, read_state);
569         return read_state;
570 }
571
572 #ifdef AO_SCHEME_FEATURE_VECTOR
573 #define is_open(t) ((t) == OPEN || (t) == OPEN_VECTOR)
574 #else
575 #define is_open(t) ((t) == OPEN)
576 #endif
577
578 ao_poly
579 ao_scheme_read(FILE *in)
580 {
581         struct ao_scheme_atom   *atom;
582         struct ao_scheme_string *string;
583         int                     read_state;
584         ao_poly                 v = AO_SCHEME_NIL;
585
586         ao_scheme_read_list = 0;
587         read_state = 0;
588         ao_scheme_read_cons = ao_scheme_read_cons_tail = ao_scheme_read_stack = NULL;
589         for (;;) {
590                 parse_token = lex(in);
591                 while (is_open(parse_token)) {
592 #ifdef AO_SCHEME_FEATURE_VECTOR
593                         if (parse_token == OPEN_VECTOR)
594                                 read_state |= READ_SAW_VECTOR;
595 #endif
596                         if (!push_read_stack(read_state))
597                                 return AO_SCHEME_NIL;
598                         ao_scheme_read_list++;
599                         read_state = 0;
600                         parse_token = lex(in);
601                 }
602
603                 switch (parse_token) {
604                 case END:
605                 default:
606                         if (ao_scheme_read_list)
607                                 ao_scheme_error(AO_SCHEME_EOF, "unexpected end of file");
608                         return _ao_scheme_atom_eof;
609                         break;
610                 case NAME:
611                         atom = ao_scheme_atom_intern(token_string);
612                         if (atom)
613                                 v = ao_scheme_atom_poly(atom);
614                         else
615                                 v = AO_SCHEME_NIL;
616                         break;
617                 case NUM:
618                         v = ao_scheme_integer_poly(token_int);
619                         break;
620 #ifdef AO_SCHEME_FEATURE_FLOAT
621                 case FLOAT:
622                         v = ao_scheme_float_get(token_float);
623                         break;
624 #endif
625                 case TRUE_TOKEN:
626                         v = _ao_scheme_bool_true;
627                         break;
628                 case FALSE_TOKEN:
629                         v = _ao_scheme_bool_false;
630                         break;
631                 case STRING:
632                         string = ao_scheme_string_new(token_string);
633                         if (string)
634                                 v = ao_scheme_string_poly(string);
635                         else
636                                 v = AO_SCHEME_NIL;
637                         break;
638                 case QUOTE:
639 #ifdef AO_SCHEME_FEATURE_QUASI
640                 case QUASIQUOTE:
641                 case UNQUOTE:
642                 case UNQUOTE_SPLICING:
643 #endif
644                         if (!push_read_stack(read_state))
645                                 return AO_SCHEME_NIL;
646                         ao_scheme_read_list++;
647                         read_state = READ_IN_QUOTE;
648                         switch (parse_token) {
649                         case QUOTE:
650                                 v = _ao_scheme_atom_quote;
651                                 break;
652 #ifdef AO_SCHEME_FEATURE_QUASI
653                         case QUASIQUOTE:
654                                 v = _ao_scheme_atom_quasiquote;
655                                 break;
656                         case UNQUOTE:
657                                 v = _ao_scheme_atom_unquote;
658                                 break;
659                         case UNQUOTE_SPLICING:
660                                 v = _ao_scheme_atom_unquote2dsplicing;
661                                 break;
662 #endif
663                         }
664                         break;
665                 case CLOSE:
666                         if (!ao_scheme_read_list) {
667                                 v = AO_SCHEME_NIL;
668                                 break;
669                         }
670                         v = ao_scheme_cons_poly(ao_scheme_read_cons);
671                         --ao_scheme_read_list;
672                         read_state = pop_read_stack();
673 #ifdef AO_SCHEME_FEATURE_VECTOR
674                         if (read_state & READ_SAW_VECTOR) {
675                                 v = ao_scheme_vector_poly(ao_scheme_list_to_vector(ao_scheme_poly_cons(v)));
676                                 read_state &= ~READ_SAW_VECTOR;
677                         }
678 #endif
679                         break;
680                 case DOT:
681                         if (!ao_scheme_read_list) {
682                                 ao_scheme_error(AO_SCHEME_INVALID, ". outside of cons");
683                                 return AO_SCHEME_NIL;
684                         }
685                         if (!ao_scheme_read_cons) {
686                                 ao_scheme_error(AO_SCHEME_INVALID, ". first in cons");
687                                 return AO_SCHEME_NIL;
688                         }
689                         read_state |= READ_SAW_DOT;
690                         continue;
691                 }
692
693                 /* loop over QUOTE ends */
694                 for (;;) {
695                         if (!ao_scheme_read_list)
696                                 return v;
697
698                         if (read_state & READ_DONE_DOT) {
699                                 ao_scheme_error(AO_SCHEME_INVALID, ". not last in cons");
700                                 return AO_SCHEME_NIL;
701                         }
702
703                         if (read_state & READ_SAW_DOT) {
704                                 read_state |= READ_DONE_DOT;
705                                 ao_scheme_read_cons_tail->cdr = v;
706                         } else {
707                                 struct ao_scheme_cons   *read = ao_scheme_cons_cons(v, AO_SCHEME_NIL);
708                                 if (!read)
709                                         return AO_SCHEME_NIL;
710
711                                 if (ao_scheme_read_cons_tail)
712                                         ao_scheme_read_cons_tail->cdr = ao_scheme_cons_poly(read);
713                                 else
714                                         ao_scheme_read_cons = read;
715                                 ao_scheme_read_cons_tail = read;
716                         }
717
718                         if (!(read_state & READ_IN_QUOTE) || !ao_scheme_read_cons->cdr)
719                                 break;
720
721                         v = ao_scheme_cons_poly(ao_scheme_read_cons);
722                         --ao_scheme_read_list;
723                         read_state = pop_read_stack();
724                 }
725         }
726         return v;
727 }