/* read.c: Input functions and primitives; the Scheme reader/parser. * * $Id$ * * Copyright 1990, 1991, 1992, 1993, 1994, 1995, Oliver Laumann, Berlin * Copyright 2002, 2003 Sam Hocevar , Paris * * This software was derived from Elk 1.2, which was Copyright 1987, 1988, * 1989, Nixdorf Computer AG and TELES GmbH, Berlin (Elk 1.2 has been written * by Oliver Laumann for TELES Telematic Services, Berlin, in a joint project * between TELES and Nixdorf Microprocessor Engineering, Berlin). * * Oliver Laumann, TELES GmbH, Nixdorf Computer AG and Sam Hocevar, as co- * owners or individual owners of copyright in this software, grant to any * person or company a worldwide, royalty free, license to * * i) copy this software, * ii) prepare derivative works based on this software, * iii) distribute copies of this software or derivative works, * iv) perform this software, or * v) display this software, * * provided that this notice is not removed and that neither Oliver Laumann * nor Teles nor Nixdorf are deemed to have made any representations as to * the suitability of this software for any purpose nor are held responsible * for any defects of this software. * * THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ #include "config.h" #include #include #include #ifdef FLUSH_TIOCFLUSH # include #else #ifdef FLUSH_TCFLSH # include #endif #endif #if defined(FIONREAD_IN_TERMIOS_H) # include #elif defined(FIONREAD_IN_SYS_IOCTL_H) # include #elif defined(FIONREAD_IN_SYS_FILIO_H) # include #endif #include "kernel.h" extern void Flush_Output (Object); extern char *index(); extern double atof(); int Skip_Comment (Object); void Reader_Error (Object, char *) elk_attribute(__noreturn__); Object Sym_Quote, Sym_Quasiquote, Sym_Unquote, Sym_Unquote_Splicing; #define Octal(c) ((c) >= '0' && (c) <= '7') static READFUN Readers[256]; static char *Read_Buf; static int Read_Size, Read_Max; #define Read_Reset() (Read_Size = 0) #define Read_Store(c) (Read_Size == Read_Max ? \ (Read_Grow(), Read_Buf[Read_Size++] = (c)) : (Read_Buf[Read_Size++] = (c))) static void Read_Grow () { Read_Max *= 2; Read_Buf = Safe_Realloc (Read_Buf, Read_Max); } Object General_Read(), Read_Sequence(), Read_Atom(), Read_Special(); Object Read_String(), Read_Sharp(), Read_True(), Read_False(), Read_Void(); Object Read_Kludge(), Read_Vector(), Read_Radix(), Read_Char(); void Init_Read () { Define_Symbol (&Sym_Quote, "quote"); Define_Symbol (&Sym_Quasiquote, "quasiquote"); Define_Symbol (&Sym_Unquote, "unquote"); Define_Symbol (&Sym_Unquote_Splicing, "unquote-splicing"); Readers['t'] = Readers['T'] = Read_True; Readers['f'] = Readers['F'] = Read_False; Readers['v'] = Readers['V'] = Read_Void; Readers['!'] = Read_Kludge; /* for interpreter files */ Readers['('] = Read_Vector; Readers['b'] = Readers['B'] = Readers['o'] = Readers['O'] = Readers['d'] = Readers['D'] = Readers['x'] = Readers['X'] = Readers['e'] = Readers['E'] = Readers['i'] = Readers['I'] = Read_Radix; Readers['\\'] = Read_Char; Read_Max = 128; Read_Buf = Safe_Malloc (Read_Max); } int String_Getc (Object port) { register struct S_Port *p; register struct S_String *s; p = PORT(port); if (p->flags & P_UNREAD) { p->flags &= ~P_UNREAD; return p->unread; } s = STRING(p->name); return p->ptr >= s->size ? EOF : s->data[p->ptr++]; } void String_Ungetc (Object port, register int c) { PORT(port)->flags |= P_UNREAD; PORT(port)->unread = c; } void Check_Input_Port (Object port) { Check_Type (port, T_Port); if (!(PORT(port)->flags & P_OPEN)) Primitive_Error ("port has been closed: ~s", port); if (!IS_INPUT(port)) Primitive_Error ("not an input port: ~s", port); } Object P_Clear_Input_Port (int argc, Object *argv) { Discard_Input (argc == 1 ? argv[0] : Curr_Input_Port); return Void; } void Discard_Input (Object port) { register FILE *f; Check_Input_Port (port); if (PORT(port)->flags & P_STRING) return; f = PORT(port)->file; #ifdef FLUSH_FPURGE (void)fpurge (f); #else #ifdef FLUSH_BSD f->_cnt = 0; f->_ptr = f->_base; #endif #endif #ifdef FLUSH_TIOCFLUSH (void)ioctl (fileno (f), TIOCFLUSH, (char *)0); #else #ifdef FLUSH_TCFLSH (void)ioctl (fileno (f), TCFLSH, (char *)0); #endif #endif } Object P_Unread_Char (int argc, Object *argv) { Object port, ch; register struct S_Port *p; ch = argv[0]; Check_Type (ch, T_Character); port = argc == 2 ? argv[1] : Curr_Input_Port; Check_Input_Port (port); p = PORT(port); if (p->flags & P_STRING) { if (p->flags & P_UNREAD) Primitive_Error ("cannot push back more than one char"); String_Ungetc (port, CHAR(ch)); } else { if (ungetc (CHAR(ch), p->file) == EOF) Primitive_Error ("failed to push back char"); } if (CHAR(ch) == '\n' && PORT(port)->lno > 1) PORT(port)->lno--; return ch; } Object P_Read_Char (int argc, Object *argv) { Object port; register FILE *f; register int c, str, flags; port = argc == 1 ? argv[0] : Curr_Input_Port; Check_Input_Port (port); f = PORT(port)->file; flags = PORT(port)->flags; str = flags & P_STRING; Reader_Getc; Reader_Tweak_Stream; return c == EOF ? Eof : Make_Char (c); } Object P_Peek_Char (int argc, Object *argv) { Object a[2]; a[0] = P_Read_Char (argc, argv); if (argc == 1) a[1] = argv[0]; return EQ(a[0], Eof) ? Eof : P_Unread_Char (argc+1, a); } /* char-ready? cannot be implemented correctly based on FILE pointers. * The following is only an approximation; even if FIONREAD is supported, * the primitive may return #f although a call to read-char would not block. */ Object P_Char_Readyp (int argc, Object *argv) { Object port; port = argc == 1 ? argv[0] : Curr_Input_Port; Check_Input_Port (port); if (PORT(port)->flags & P_STRING || feof (PORT(port)->file)) return True; #ifdef FIONREAD { long num = 0; (void)ioctl (fileno (PORT(port)->file), FIONREAD, (char *)&num); if (num != 0) return True; } #endif return False; } Object P_Read_String (int argc, Object *argv) { Object port; register FILE *f; register int c, str; port = argc == 1 ? argv[0] : Curr_Input_Port; Check_Input_Port (port); f = PORT(port)->file; str = PORT(port)->flags & P_STRING; Read_Reset (); while (1) { Reader_Getc; if (c == '\n' || c == EOF) break; Read_Store (c); } Reader_Tweak_Stream; return c == EOF ? Eof : Make_String (Read_Buf, Read_Size); } Object P_Read (int argc, Object *argv) { return General_Read (argc == 1 ? argv[0] : Curr_Input_Port, 0); } Object General_Read (Object port, int konst) { register FILE *f; register int c, str; Object ret; Check_Input_Port (port); Flush_Output (Curr_Output_Port); f = PORT(port)->file; str = PORT(port)->flags & P_STRING; while (1) { Reader_Getc; if (c == EOF) { ret = Eof; break; } if (Whitespace (c)) continue; if (c == ';') { comment: if (Skip_Comment (port) == EOF) { ret = Eof; break; } continue; } if (c == '(') { ret = Read_Sequence (port, 0, konst); } else if (c == '#') { ret = Read_Sharp (port, konst); if (TYPE(ret) == T_Special) /* it was a #! */ goto comment; } else { Reader_Ungetc; ret = Read_Atom (port, konst); } break; } Reader_Tweak_Stream; return ret; } int Skip_Comment (Object port) { register FILE *f; register int c, str; f = PORT(port)->file; str = PORT(port)->flags & P_STRING; do { Reader_Getc; } while (c != '\n' && c != EOF); return c; } Object Read_Atom (Object port, int konst) { Object ret; ret = Read_Special (port, konst); if (TYPE(ret) == T_Special) Reader_Error (port, "syntax error"); return ret; } Object Read_Special (Object port, int konst) { Object ret; register int c, str; register FILE *f; #define READ_QUOTE(sym) \ ( ret = Read_Atom (port, konst),\ konst ? (ret = Const_Cons (ret, Null), Const_Cons (sym, ret))\ : (ret = Cons (ret, Null), Cons (sym, ret))) f = PORT(port)->file; str = PORT(port)->flags & P_STRING; again: Reader_Getc; switch (c) { case EOF: eof: Reader_Tweak_Stream; Reader_Error (port, "premature end of file"); case ';': if (Skip_Comment (port) == EOF) goto eof; goto again; case ')': SET(ret, T_Special, c); return ret; case '(': return Read_Sequence (port, 0, konst); case '\'': return READ_QUOTE(Sym_Quote); case '`': return READ_QUOTE(Sym_Quasiquote); case ',': Reader_Getc; if (c == EOF) goto eof; if (c == '@') { return READ_QUOTE(Sym_Unquote_Splicing); } else { Reader_Ungetc; return READ_QUOTE(Sym_Unquote); } case '"': return Read_String (port, konst); case '#': ret = Read_Sharp (port, konst); if (TYPE(ret) == T_Special) goto again; return ret; default: if (Whitespace (c)) goto again; Read_Reset (); if (c == '.') { Reader_Getc; if (c == EOF) goto eof; if (Whitespace (c)) { Reader_Ungetc; SET(ret, T_Special, '.'); return ret; } Read_Store ('.'); } while (!Whitespace (c) && !Delimiter (c) && c != EOF) { if (c == '\\') { Reader_Getc; if (c == EOF) break; } Read_Store (c); Reader_Getc; } Read_Store ('\0'); if (c != EOF) Reader_Ungetc; ret = Parse_Number (port, Read_Buf, 10); if (Nullp (ret)) ret = Intern (Read_Buf); return ret; } /*NOTREACHED*/ } Object Read_Sequence (Object port, int vec, int konst) { Object ret, e, tail, t; GC_Node3; ret = tail = Null; GC_Link3 (ret, tail, port); while (1) { e = Read_Special (port, konst); if (TYPE(e) == T_Special) { if (CHAR(e) == ')') { GC_Unlink; return ret; } if (vec) Reader_Error (port, "wrong syntax in vector"); if (CHAR(e) == '.') { if (Nullp (tail)) { ret = Read_Atom (port, konst); } else { e = Read_Atom (port, konst); /* * Possibly modifying pure cons. Must be fixed! */ Cdr (tail) = e; } e = Read_Special (port, konst); if (TYPE(e) == T_Special && CHAR(e) == ')') { GC_Unlink; return ret; } Reader_Error (port, "dot in wrong context"); } Reader_Error (port, "syntax error"); } if (konst) t = Const_Cons (e, Null); else t = Cons (e, Null); if (!Nullp (tail)) /* * Possibly modifying pure cons. Must be fixed! */ Cdr (tail) = t; else ret = t; tail = t; } /*NOTREACHED*/ } Object Read_String (Object port, int konst) { register FILE *f; register int n, c, oc, str; Read_Reset (); f = PORT(port)->file; str = PORT(port)->flags & P_STRING; while (1) { Reader_Getc; if (c == EOF) { eof: Reader_Tweak_Stream; Reader_Error (port, "end of file in string"); } if (c == '\\') { Reader_Getc; switch (c) { case EOF: goto eof; case 'b': c = '\b'; break; case 't': c = '\t'; break; case 'r': c = '\r'; break; case 'n': c = '\n'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': oc = n = 0; do { oc <<= 3; oc += c - '0'; Reader_Getc; if (c == EOF) goto eof; } while (Octal (c) && ++n <= 2); Reader_Ungetc; c = oc; } } else if (c == '"') break; Read_Store (c); } return General_Make_String (Read_Buf, Read_Size, konst); } Object Read_Sharp (Object port, int konst) { int c, str; FILE *f; char buf[32]; f = PORT(port)->file; str = PORT(port)->flags & P_STRING; Reader_Getc; if (c == EOF) Reader_Sharp_Eof; if (!Readers[c]) { sprintf (buf, "no reader for syntax #%c", c); Reader_Error (port, buf); } return Readers[c](port, c, konst); } /*ARGSUSED*/ Object Read_True (Object port, int chr, int konst) { return True; } /*ARGSUSED*/ Object Read_False (Object port, int chr, int konst) { return False; } /*ARGSUSED*/ Object Read_Void (Object port, int chr, int konst) { Object ret; ret = Const_Cons (Void, Null); return Const_Cons (Sym_Quote, ret); } /*ARGSUSED*/ Object Read_Kludge (Object port, int chr, int konst) { return Special; } /*ARGSUSED*/ Object Read_Vector (Object port, int chr, int konst) { return List_To_Vector (Read_Sequence (port, 1, konst), konst); } /*ARGSUSED*/ Object Read_Radix (Object port, int chr, int konst) { int c, str; FILE *f; Object ret; Read_Reset (); f = PORT(port)->file; str = PORT(port)->flags & P_STRING; Read_Store ('#'); Read_Store (chr); while (1) { Reader_Getc; if (c == EOF) Reader_Sharp_Eof; if (Whitespace (c) || Delimiter (c)) break; Read_Store (c); } Reader_Ungetc; Read_Store ('\0'); ret = Parse_Number (port, Read_Buf, 10); if (Nullp (ret)) Reader_Error (port, "radix not followed by a valid number"); return ret; } /*ARGSUSED*/ Object Read_Char (Object port, int chr, int konst) { int c, str; FILE *f; char buf[10], *p = buf; f = PORT(port)->file; str = PORT(port)->flags & P_STRING; Reader_Getc; if (c == EOF) Reader_Sharp_Eof; *p++ = c; while (1) { Reader_Getc; if (c == EOF) Reader_Sharp_Eof; if (Whitespace (c) || Delimiter (c)) break; if (p == buf+9) Reader_Error (port, "syntax error in character constant"); *p++ = c; } Reader_Ungetc; if (p == buf+1) return Make_Char (*buf); *p = '\0'; if (p == buf+3) { for (c = 0, p = buf; p < buf+3 && Octal (*p); p++) c = c << 3 | (*p - '0'); if (p == buf+3) return Make_Char (c); } for (p = buf; *p; p++) if (isupper (*p)) *p = tolower (*p); if (strcmp (buf, "space") == 0) return Make_Char (' '); if (strcmp (buf, "newline") == 0) return Make_Char ('\n'); if (strcmp (buf, "return") == 0) return Make_Char ('\r'); if (strcmp (buf, "tab") == 0) return Make_Char ('\t'); if (strcmp (buf, "formfeed") == 0) return Make_Char ('\f'); if (strcmp (buf, "backspace") == 0) return Make_Char ('\b'); Reader_Error (port, "syntax error in character constant"); /*NOTREACHED*/ } void Define_Reader (int c, READFUN fun) { if (Readers[c] && Readers[c] != fun) Primitive_Error ("reader for `~a' already defined", Make_Char (c)); Readers[c] = fun; } Object Parse_Number (Object port, char const *buf, int radix) { char const *p; int c, i; int mdigit = 0, edigit = 0, expo = 0, neg = 0, point = 0; int gotradix = 0, exact = 0, inexact = 0; unsigned int max; int maxdig; Object ret; for ( ; *buf == '#'; buf++) { switch (*++buf) { case 'b': case 'B': if (gotradix++) return Null; radix = 2; break; case 'o': case 'O': if (gotradix++) return Null; radix = 8; break; case 'd': case 'D': if (gotradix++) return Null; radix = 10; break; case 'x': case 'X': if (gotradix++) return Null; radix = 16; break; case 'e': case 'E': if (exact++ || inexact) return Null; break; case 'i': case 'I': if (inexact++ || exact) return Null; break; default: return Null; } } p = buf; if (*p == '+' || (neg = *p == '-')) p++; for ( ; (c = *p); p++) { if (c == '.') { if (expo || point++) return Null; } else if (radix != 16 && (c == 'e' || c == 'E')) { if (expo++) return Null; if (p[1] == '+' || p[1] == '-') p++; } else if (radix == 16 && !index ("0123456789abcdefABCDEF", c)) { return Null; } else if (radix < 16 && (c < '0' || c > '0' + radix-1)) { return Null; } else { if (expo) edigit++; else mdigit++; } } if (!mdigit || (expo && !edigit)) return Null; if (point || expo) { if (radix != 10) { if (Nullp (port)) return Null; Reader_Error (port, "reals must be given in decimal"); } /* Lacking ratnums, there's nothing we can do if #e has been * specified-- just return the inexact number. */ return Make_Flonum (atof (buf)); } max = (neg ? -(unsigned int)INT_MIN : INT_MAX); maxdig = max % radix; max /= radix; for (i = 0, p = buf; (c = *p); p++) { if (c == '-' || c == '+') { buf++; continue; } if (radix == 16) { if (isupper (c)) c = tolower (c); if (c >= 'a') c = '9' + c - 'a' + 1; } c -= '0'; if ((unsigned int)i > max || ((unsigned int)i == max && c > maxdig)) { ret = Make_Bignum (buf, neg, radix); return inexact ? Make_Flonum (Bignum_To_Double (ret)) : ret; } i *= radix; i += c; } if (neg) i = -i; return inexact ? Make_Flonum ((double)i) : Make_Integer (i); } void Reader_Error (Object port, char *msg) { char buf[100]; if (PORT(port)->flags & P_STRING) { sprintf (buf, "[string-port]: %u: %s", PORT(port)->lno, msg); Primitive_Error (buf); } else { sprintf (buf, "~s: %u: %s", PORT(port)->lno, msg); Primitive_Error (buf, PORT(port)->name); } }