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