xenocara/app/xedit/lisp/read.c
2015-05-10 10:07:47 +00:00

2056 lines
47 KiB
C

/*
* Copyright (c) 2002 by The XFree86 Project, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the XFree86 Project shall
* not be used in advertising or otherwise to promote the sale, use or other
* dealings in this Software without prior written authorization from the
* XFree86 Project.
*
* Author: Paulo César Pereira de Andrade
*/
/* $XFree86: xc/programs/xedit/lisp/read.c,v 1.36tsi Exp $ */
#include <errno.h>
#include "lisp/read.h"
#include "lisp/package.h"
#include "lisp/write.h"
#include <fcntl.h>
#include <stdarg.h>
/* This should be visible only in read.c, but if an error is generated,
* the current code in write.c will print it as #<ERROR> */
#define LABEL_BIT_COUNT 8
#define LABEL_BIT_MASK 0xff
#define MAX_LABEL_VALUE ((1L << (sizeof(long) * 8 - 9)) - 1)
#define READLABEL(label) \
(LispObj*)(((label) << LABEL_BIT_COUNT) | READLABEL_MASK)
#define READLABELP(object) \
(((unsigned long)(object) & LABEL_BIT_MASK) == READLABEL_MASK)
#define READLABEL_VALUE(object) \
((long)(object) >> LABEL_BIT_COUNT)
#define READ_ENTER() \
LispObj *read__stream = SINPUT; \
int read__line = LispGetLine(read__stream)
#define READ_ERROR0(format) \
LispReadError(read__stream, read__line, format)
#define READ_ERROR1(format, arg1) \
LispReadError(read__stream, read__line, format, arg1)
#define READ_ERROR2(format, arg1, arg2) \
LispReadError(read__stream, read__line, format, arg1, arg2)
#define READ_ERROR_EOF() READ_ERROR0("unexpected end of input")
#define READ_ERROR_FIXNUM() READ_ERROR0("number is not a fixnum")
#define READ_ERROR_INVARG() READ_ERROR0("invalid argument")
#ifdef __UNIXOS2__
# define finite(x) isfinite(x)
#endif
/*
* Types
*/
typedef struct _object_info {
long label; /* the read label of this object */
LispObj *object; /* the resulting object */
long num_circles; /* references to object before it was completely read */
} object_info;
typedef struct _read_info {
int level; /* level of open parentheses */
int nodot; /* flag set when reading a "special" list */
int discard; /* flag used when reading an unavailable feature */
long circle_count; /* if non zero, must resolve some labels */
/* information for #<number>= and #<number># */
object_info *objects;
long num_objects;
/* could use only the objects field as all circular data is known,
* but check every object so that circular/shared references generated
* by evaluations would not cause an infinite loop at read time */
LispObj **circles;
long num_circles;
} read_info;
/*
* Protypes
*/
static LispObj *LispReadChar(LispBuiltin*, int);
static int LispGetLine(LispObj*);
#ifdef __GNUC__
#define PRINTF_FORMAT __attribute__ ((format (printf, 3, 4)))
#else
#define PRINTF_FORMAT /**/
#endif
static void LispReadError(LispObj*, int, const char*, ...);
#undef PRINTF_FORMAT
static void LispReadFixCircle(LispObj*, read_info*);
static LispObj *LispReadLabelCircle(LispObj*, read_info*);
static int LispReadCheckCircle(LispObj*, read_info*);
static LispObj *LispDoRead(read_info*);
static int LispSkipWhiteSpace(void);
static LispObj *LispReadList(read_info*);
static LispObj *LispReadQuote(read_info*);
static LispObj *LispReadBackquote(read_info*);
static LispObj *LispReadCommaquote(read_info*);
static LispObj *LispReadObject(int, read_info*);
static LispObj *LispParseAtom(char*, char*, int, int, LispObj*, int);
static LispObj *LispParseNumber(char*, int, LispObj*, int);
static int StringInRadix(char*, int, int);
static int AtomSeparator(int, int, int);
static LispObj *LispReadVector(read_info*);
static LispObj *LispReadMacro(read_info*);
static LispObj *LispReadFunction(read_info*);
static LispObj *LispReadRational(int, read_info*);
static LispObj *LispReadCharacter(read_info*);
static void LispSkipComment(void);
static LispObj *LispReadEval(read_info*);
static LispObj *LispReadComplex(read_info*);
static LispObj *LispReadPathname(read_info*);
static LispObj *LispReadStruct(read_info*);
static LispObj *LispReadMacroArg(read_info*);
static LispObj *LispReadArray(long, read_info*);
static LispObj *LispReadFeature(int, read_info*);
static LispObj *LispEvalFeature(LispObj*);
/*
* Initialization
*/
static const char * const Char_Nul[] = {"Null", "Nul", NULL};
static const char * const Char_Soh[] = {"Soh", NULL};
static const char * const Char_Stx[] = {"Stx", NULL};
static const char * const Char_Etx[] = {"Etx", NULL};
static const char * const Char_Eot[] = {"Eot", NULL};
static const char * const Char_Enq[] = {"Enq", NULL};
static const char * const Char_Ack[] = {"Ack", NULL};
static const char * const Char_Bel[] = {"Bell", "Bel", NULL};
static const char * const Char_Bs[] = {"Backspace", "Bs", NULL};
static const char * const Char_Tab[] = {"Tab", NULL};
static const char * const Char_Nl[] = {"Newline", "Nl", "Lf", "Linefeed", NULL};
static const char * const Char_Vt[] = {"Vt", NULL};
static const char * const Char_Np[] = {"Page", "Np", NULL};
static const char * const Char_Cr[] = {"Return", "Cr", NULL};
static const char * const Char_Ff[] = {"So", "Ff", NULL};
static const char * const Char_Si[] = {"Si", NULL};
static const char * const Char_Dle[] = {"Dle", NULL};
static const char * const Char_Dc1[] = {"Dc1", NULL};
static const char * const Char_Dc2[] = {"Dc2", NULL};
static const char * const Char_Dc3[] = {"Dc3", NULL};
static const char * const Char_Dc4[] = {"Dc4", NULL};
static const char * const Char_Nak[] = {"Nak", NULL};
static const char * const Char_Syn[] = {"Syn", NULL};
static const char * const Char_Etb[] = {"Etb", NULL};
static const char * const Char_Can[] = {"Can", NULL};
static const char * const Char_Em[] = {"Em", NULL};
static const char * const Char_Sub[] = {"Sub", NULL};
static const char * const Char_Esc[] = {"Escape", "Esc", NULL};
static const char * const Char_Fs[] = {"Fs", NULL};
static const char * const Char_Gs[] = {"Gs", NULL};
static const char * const Char_Rs[] = {"Rs", NULL};
static const char * const Char_Us[] = {"Us", NULL};
static const char * const Char_Sp[] = {"Space", "Sp", NULL};
static const char * const Char_Del[] = {"Rubout", "Del", "Delete", NULL};
const LispCharInfo LispChars[256] = {
{Char_Nul},
{Char_Soh},
{Char_Stx},
{Char_Etx},
{Char_Eot},
{Char_Enq},
{Char_Ack},
{Char_Bel},
{Char_Bs},
{Char_Tab},
{Char_Nl},
{Char_Vt},
{Char_Np},
{Char_Cr},
{Char_Ff},
{Char_Si},
{Char_Dle},
{Char_Dc1},
{Char_Dc2},
{Char_Dc3},
{Char_Dc4},
{Char_Nak},
{Char_Syn},
{Char_Etb},
{Char_Can},
{Char_Em},
{Char_Sub},
{Char_Esc},
{Char_Fs},
{Char_Gs},
{Char_Rs},
{Char_Us},
{Char_Sp},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{Char_Del},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
{NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}
};
Atom_id Sand, Sor, Snot;
/*
* Implementation
*/
LispObj *
Lisp_Read(LispBuiltin *builtin)
/*
read &optional input-stream eof-error-p eof-value recursive-p
*/
{
LispObj *result;
LispObj *input_stream, *eof_error_p, *eof_value;
eof_value = ARGUMENT(2);
eof_error_p = ARGUMENT(1);
input_stream = ARGUMENT(0);
if (input_stream == UNSPEC)
input_stream = NIL;
else if (input_stream != NIL) {
CHECK_STREAM(input_stream);
else if (!input_stream->data.stream.readable)
LispDestroy("%s: stream %s is not readable",
STRFUN(builtin), STROBJ(input_stream));
LispPushInput(input_stream);
}
else if (CONSP(lisp__data.input_list)) {
input_stream = STANDARD_INPUT;
LispPushInput(input_stream);
}
if (eof_value == UNSPEC)
eof_value = NIL;
result = LispRead();
if (input_stream != NIL)
LispPopInput(input_stream);
if (result == NULL) {
if (eof_error_p != NIL)
LispDestroy("%s: EOF reading stream %s",
STRFUN(builtin), STROBJ(input_stream));
else
result = eof_value;
}
return (result);
}
static LispObj *
LispReadChar(LispBuiltin *builtin, int nohang)
{
int character;
LispObj *input_stream, *eof_error_p, *eof_value;
eof_value = ARGUMENT(2);
eof_error_p = ARGUMENT(1);
input_stream = ARGUMENT(0);
if (input_stream == UNSPEC)
input_stream = NIL;
else if (input_stream != NIL) {
CHECK_STREAM(input_stream);
}
else
input_stream = lisp__data.input;
if (eof_value == UNSPEC)
eof_value = NIL;
character = EOF;
if (input_stream->data.stream.readable) {
LispFile *file = NULL;
switch (input_stream->data.stream.type) {
case LispStreamStandard:
case LispStreamFile:
file = FSTREAMP(input_stream);
break;
case LispStreamPipe:
file = IPSTREAMP(input_stream);
break;
case LispStreamString:
character = LispSgetc(SSTREAMP(input_stream));
break;
default:
break;
}
if (file != NULL) {
if (file->available || file->offset < file->length)
character = LispFgetc(file);
else {
if (nohang && !file->nonblock) {
if (fcntl(file->descriptor, F_SETFL, O_NONBLOCK) < 0)
LispDestroy("%s: fcntl(%d): %s",
STRFUN(builtin), file->descriptor,
strerror(errno));
file->nonblock = 1;
}
else if (!nohang && file->nonblock) {
if (fcntl(file->descriptor, F_SETFL, 0) < 0)
LispDestroy("%s: fcntl(%d): %s",
STRFUN(builtin), file->descriptor,
strerror(errno));
file->nonblock = 0;
}
if (nohang) {
unsigned char ch;
if (read(file->descriptor, &ch, 1) == 1)
character = ch;
else if (errno == EAGAIN)
return (NIL); /* XXX no character available */
else
character = EOF;
}
else
character = LispFgetc(file);
}
}
}
else
LispDestroy("%s: stream %s is unreadable",
STRFUN(builtin), STROBJ(input_stream));
if (character == EOF) {
if (eof_error_p != NIL)
LispDestroy("%s: EOF reading stream %s",
STRFUN(builtin), STROBJ(input_stream));
return (eof_value);
}
return (SCHAR(character));
}
LispObj *
Lisp_ReadChar(LispBuiltin *builtin)
/*
read-char &optional input-stream eof-error-p eof-value recursive-p
*/
{
return (LispReadChar(builtin, 0));
}
LispObj *
Lisp_ReadCharNoHang(LispBuiltin *builtin)
/*
read-char-no-hang &optional input-stream eof-error-p eof-value recursive-p
*/
{
return (LispReadChar(builtin, 1));
}
LispObj *
Lisp_ReadLine(LispBuiltin *builtin)
/*
read-line &optional input-stream eof-error-p eof-value recursive-p
*/
{
char *string;
int ch, length;
LispObj *result, *status = NIL;
LispObj *input_stream, *eof_error_p, *eof_value;
eof_value = ARGUMENT(2);
eof_error_p = ARGUMENT(1);
input_stream = ARGUMENT(0);
if (input_stream == UNSPEC)
input_stream = NIL;
else if (input_stream == NIL)
input_stream = STANDARD_INPUT;
else {
CHECK_STREAM(input_stream);
}
if (eof_value == UNSPEC)
eof_value = NIL;
result = NIL;
string = NULL;
length = 0;
if (!input_stream->data.stream.readable)
LispDestroy("%s: stream %s is unreadable",
STRFUN(builtin), STROBJ(input_stream));
if (input_stream->data.stream.type == LispStreamString) {
char *start, *end, *ptr;
if (SSTREAMP(input_stream)->input >=
SSTREAMP(input_stream)->length) {
if (eof_error_p != NIL)
LispDestroy("%s: EOS found reading %s",
STRFUN(builtin), STROBJ(input_stream));
status = T;
result = eof_value;
goto read_line_done;
}
start = SSTREAMP(input_stream)->string +
SSTREAMP(input_stream)->input;
end = SSTREAMP(input_stream)->string +
SSTREAMP(input_stream)->length;
/* Search for a newline */
for (ptr = start; *ptr != '\n' && ptr < end; ptr++)
;
if (ptr == end)
status = T;
else if (!SSTREAMP(input_stream)->binary)
++SSTREAMP(input_stream)->line;
length = ptr - start;
string = LispMalloc(length + 1);
memcpy(string, start, length);
string[length] = '\0';
result = LSTRING2(string, length);
/* macro LSTRING2 does not make a copy of it's arguments, and
* calls LispMused on it. */
SSTREAMP(input_stream)->input += length + (status == NIL);
}
else /*if (input_stream->data.stream.type == LispStreamFile ||
input_stream->data.stream.type == LispStreamStandard ||
input_stream->data.stream.type == LispStreamPipe)*/ {
LispFile *file;
if (input_stream->data.stream.type == LispStreamPipe)
file = IPSTREAMP(input_stream);
else
file = FSTREAMP(input_stream);
if (file->nonblock) {
if (fcntl(file->descriptor, F_SETFL, 0) < 0)
LispDestroy("%s: fcntl: %s",
STRFUN(builtin), strerror(errno));
file->nonblock = 0;
}
while (1) {
ch = LispFgetc(file);
if (ch == EOF) {
if (length)
break;
if (eof_error_p != NIL)
LispDestroy("%s: EOF found reading %s",
STRFUN(builtin), STROBJ(input_stream));
if (string)
LispFree(string);
status = T;
result = eof_value;
goto read_line_done;
}
else if (ch == '\n')
break;
else if ((length % 64) == 0)
string = LispRealloc(string, length + 64);
string[length++] = ch;
}
if (string) {
if ((length % 64) == 0)
string = LispRealloc(string, length + 1);
string[length] = '\0';
result = LSTRING2(string, length);
}
else
result = STRING("");
}
read_line_done:
RETURN(0) = status;
RETURN_COUNT = 1;
return (result);
}
LispObj *
LispRead(void)
{
READ_ENTER();
read_info info;
LispObj *result, *code = COD;
info.level = info.nodot = info.discard = 0;
info.circle_count = 0;
info.objects = NULL;
info.num_objects = 0;
result = LispDoRead(&info);
/* fix circular/shared lists, note that this is done when returning to
* the toplevel, so, if some circular/shared reference was evaluated,
* it should have generated an expected error */
if (info.num_objects) {
if (info.circle_count) {
info.circles = NULL;
info.num_circles = 0;
LispReadFixCircle(result, &info);
if (info.num_circles)
LispFree(info.circles);
}
LispFree(info.objects);
}
if (result == EOLIST)
READ_ERROR0("object cannot start with #\\)");
else if (result == DOT)
READ_ERROR0("dot allowed only on lists");
if (result != NULL && POINTERP(result)) {
if (code == NIL)
COD = result;
else
COD = CONS(COD, result);
}
return (result);
}
static int
LispGetLine(LispObj *stream)
{
int line = -1;
if (STREAMP(stream)) {
switch (stream->data.stream.type) {
case LispStreamStandard:
case LispStreamFile:
if (!FSTREAMP(stream)->binary)
line = FSTREAMP(stream)->line;
break;
case LispStreamPipe:
if (!IPSTREAMP(stream)->binary)
line = IPSTREAMP(stream)->line;
break;
case LispStreamString:
if (!SSTREAMP(stream)->binary)
line = SSTREAMP(stream)->line;
break;
default:
break;
}
}
else if (stream == NIL && !Stdin->binary)
line = Stdin->line;
return (line);
}
static void
LispReadError(LispObj *stream, int line, const char *fmt, ...)
{
char string[128];
const char *buffer_string;
LispObj *buffer = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
int length;
va_list ap;
va_start(ap, fmt);
vsnprintf(string, sizeof(string), fmt, ap);
va_end(ap);
LispFwrite(Stderr, "*** Reading ", 12);
LispWriteObject(buffer, stream);
buffer_string = LispGetSstring(SSTREAMP(buffer), &length);
LispFwrite(Stderr, buffer_string, length);
LispFwrite(Stderr, " at line ", 9);
if (line < 0)
LispFwrite(Stderr, "?\n", 2);
else {
char str[32];
sprintf(str, "%d\n", line);
LispFputs(Stderr, str);
}
LispDestroy("READ: %s", string);
}
static void
LispReadFixCircle(LispObj *object, read_info *info)
{
LispObj *cons;
fix_again:
switch (OBJECT_TYPE(object)) {
case LispCons_t:
for (cons = object;
CONSP(object);
cons = object, object = CDR(object)) {
if (READLABELP(CAR(object)))
CAR(object) = LispReadLabelCircle(CAR(object), info);
else if (LispReadCheckCircle(object, info))
return;
else
LispReadFixCircle(CAR(object), info);
}
if (READLABELP(object))
CDR(cons) = LispReadLabelCircle(object, info);
else
goto fix_again;
break;
case LispArray_t:
if (READLABELP(object->data.array.list))
object->data.array.list =
LispReadLabelCircle(object->data.array.list, info);
else if (!LispReadCheckCircle(object, info)) {
object = object->data.array.list;
goto fix_again;
}
break;
case LispStruct_t:
if (READLABELP(object->data.struc.fields))
object->data.struc.fields =
LispReadLabelCircle(object->data.struc.fields, info);
else if (!LispReadCheckCircle(object, info)) {
object = object->data.struc.fields;
goto fix_again;
}
break;
case LispQuote_t:
case LispBackquote_t:
case LispFunctionQuote_t:
if (READLABELP(object->data.quote))
object->data.quote =
LispReadLabelCircle(object->data.quote, info);
else {
object = object->data.quote;
goto fix_again;
}
break;
case LispComma_t:
if (READLABELP(object->data.comma.eval))
object->data.comma.eval =
LispReadLabelCircle(object->data.comma.eval, info);
else {
object = object->data.comma.eval;
goto fix_again;
}
break;
case LispLambda_t:
if (READLABELP(object->data.lambda.code))
object->data.lambda.code =
LispReadLabelCircle(object->data.lambda.code, info);
else if (!LispReadCheckCircle(object, info)) {
object = object->data.lambda.code;
goto fix_again;
}
break;
default:
break;
}
}
static LispObj *
LispReadLabelCircle(LispObj *label, read_info *info)
{
long i, value = READLABEL_VALUE(label);
for (i = 0; i < info->num_objects; i++)
if (info->objects[i].label == value)
return (info->objects[i].object);
LispDestroy("READ: internal error");
/*NOTREACHED*/
return (label);
}
static int
LispReadCheckCircle(LispObj *object, read_info *info)
{
long i;
for (i = 0; i < info->num_circles; i++)
if (info->circles[i] == object)
return (1);
if ((info->num_circles % 16) == 0)
info->circles = LispRealloc(info->circles, sizeof(LispObj*) *
(info->num_circles + 16));
info->circles[info->num_circles++] = object;
return (0);
}
static LispObj *
LispDoRead(read_info *info)
{
LispObj *object;
int ch = LispSkipWhiteSpace();
switch (ch) {
case '(':
object = LispReadList(info);
break;
case ')':
for (ch = LispGet(); ch != EOF && ch != '\n'; ch = LispGet()) {
if (!isspace(ch)) {
LispUnget(ch);
break;
}
}
return (EOLIST);
case EOF:
return (NULL);
case '\'':
object = LispReadQuote(info);
break;
case '`':
object = LispReadBackquote(info);
break;
case ',':
object = LispReadCommaquote(info);
break;
case '#':
object = LispReadMacro(info);
break;
default:
LispUnget(ch);
object = LispReadObject(0, info);
break;
}
return (object);
}
static LispObj *
LispReadMacro(read_info *info)
{
READ_ENTER();
LispObj *result = NULL;
int ch = LispGet();
switch (ch) {
case '(':
result = LispReadVector(info);
break;
case '\'':
result = LispReadFunction(info);
break;
case 'b':
case 'B':
result = LispReadRational(2, info);
break;
case 'o':
case 'O':
result = LispReadRational(8, info);
break;
case 'x':
case 'X':
result = LispReadRational(16, info);
break;
case '\\':
result = LispReadCharacter(info);
break;
case '|':
LispSkipComment();
result = LispDoRead(info);
break;
case '.': /* eval when compiling */
case ',': /* eval when loading */
result = LispReadEval(info);
break;
case 'c':
case 'C':
result = LispReadComplex(info);
break;
case 'p':
case 'P':
result = LispReadPathname(info);
break;
case 's':
case 'S':
result = LispReadStruct(info);
break;
case '+':
result = LispReadFeature(1, info);
break;
case '-':
result = LispReadFeature(0, info);
break;
case ':':
/* Uninterned symbol */
result = LispReadObject(1, info);
break;
default:
if (isdigit(ch)) {
LispUnget(ch);
result = LispReadMacroArg(info);
}
else if (!info->discard)
READ_ERROR1("undefined dispatch macro character #%c", ch);
break;
}
return (result);
}
static LispObj *
LispReadMacroArg(read_info *info)
{
READ_ENTER();
LispObj *result = NIL;
long i, integer;
int ch;
/* skip leading zeros */
while (ch = LispGet(), ch != EOF && isdigit(ch) && ch == '0')
;
if (ch == EOF)
READ_ERROR_EOF();
/* if ch is not a number the argument was zero */
if (isdigit(ch)) {
char stk[32], *str;
int len = 1;
stk[0] = ch;
for (;;) {
ch = LispGet();
if (!isdigit(ch))
break;
if (len + 1 >= sizeof(stk))
READ_ERROR_FIXNUM();
stk[len++] = ch;
}
stk[len] = '\0';
errno = 0;
integer = strtol(stk, &str, 10);
/* number is positive because sign is not processed here */
if (*str || errno == ERANGE || integer > MOST_POSITIVE_FIXNUM)
READ_ERROR_FIXNUM();
}
else
integer = 0;
switch (ch) {
case 'a':
case 'A':
if (integer == 1) {
/* LispReadArray and LispReadList expect
* the '(' being already read */
if ((ch = LispSkipWhiteSpace()) != '(') {
if (info->discard)
return (ch == EOF ? NULL : NIL);
READ_ERROR0("bad array specification");
}
result = LispReadVector(info);
}
else
result = LispReadArray(integer, info);
break;
case 'r':
case 'R':
result = LispReadRational(integer, info);
break;
case '=':
if (integer > MAX_LABEL_VALUE)
READ_ERROR_FIXNUM();
if (!info->discard) {
long num_objects = info->num_objects;
/* check for duplicated label */
for (i = 0; i < info->num_objects; i++) {
if (info->objects[i].label == integer)
READ_ERROR1("label #%ld# defined more than once",
integer);
}
info->objects = LispRealloc(info->objects,
sizeof(object_info) *
(num_objects + 1));
/* if this label is referenced it is a shared/circular object */
info->objects[num_objects].label = integer;
info->objects[num_objects].object = NULL;
info->objects[num_objects].num_circles = 0;
++info->num_objects;
result = LispDoRead(info);
if (READLABELP(result) && READLABEL_VALUE(result) == integer)
READ_ERROR2("incorrect syntax #%ld= #%ld#",
integer, integer);
/* any reference to it now is not shared/circular */
info->objects[num_objects].object = result;
}
else
result = LispDoRead(info);
break;
case '#':
if (integer > MAX_LABEL_VALUE)
READ_ERROR_FIXNUM();
if (!info->discard) {
/* search object */
for (i = 0; i < info->num_objects; i++) {
if (info->objects[i].label == integer) {
result = info->objects[i].object;
if (result == NULL) {
++info->objects[i].num_circles;
++info->circle_count;
result = READLABEL(integer);
}
break;
}
}
if (i == info->num_objects)
READ_ERROR1("undefined label #%ld#", integer);
}
break;
default:
if (!info->discard)
READ_ERROR1("undefined dispatch macro character #%c", ch);
break;
}
return (result);
}
static int
LispSkipWhiteSpace(void)
{
int ch;
for (;;) {
while (ch = LispGet(), isspace(ch) && ch != EOF)
;
if (ch == ';') {
while (ch = LispGet(), ch != '\n' && ch != EOF)
;
if (ch == EOF)
return (EOF);
}
else
break;
}
return (ch);
}
/* any data in the format '(' FORM ')' is read here */
static LispObj *
LispReadList(read_info *info)
{
READ_ENTER();
GC_ENTER();
LispObj *result, *cons, *object;
int dot = 0;
++info->level;
/* check for () */
object = LispDoRead(info);
if (object == EOLIST) {
--info->level;
return (NIL);
}
if (object == DOT)
READ_ERROR0("illegal start of dotted list");
result = cons = CONS(object, NIL);
/* make sure GC will not release data being read */
GC_PROTECT(result);
while ((object = LispDoRead(info)) != EOLIST) {
if (object == NULL)
READ_ERROR_EOF();
if (object == DOT) {
if (info->nodot == info->level)
READ_ERROR0("dotted list not allowed");
/* this is a dotted list */
if (dot)
READ_ERROR0("more than one . in list");
dot = 1;
}
else {
if (dot) {
/* only one object after a dot */
if (++dot > 2)
READ_ERROR0("more than one object after . in list");
RPLACD(cons, object);
}
else {
RPLACD(cons, CONS(object, NIL));
cons = CDR(cons);
}
}
}
/* this will happen if last list element was a dot */
if (dot == 1)
READ_ERROR0("illegal end of dotted list");
--info->level;
GC_LEAVE();
return (result);
}
static LispObj *
LispReadQuote(read_info *info)
{
READ_ENTER();
LispObj *quote = LispDoRead(info), *result;
if (INVALIDP(quote))
READ_ERROR_INVARG();
result = QUOTE(quote);
return (result);
}
static LispObj *
LispReadBackquote(read_info *info)
{
READ_ENTER();
LispObj *backquote = LispDoRead(info), *result;
if (INVALIDP(backquote))
READ_ERROR_INVARG();
result = BACKQUOTE(backquote);
return (result);
}
static LispObj *
LispReadCommaquote(read_info *info)
{
READ_ENTER();
LispObj *comma, *result;
int atlist = LispGet();
if (atlist == EOF)
READ_ERROR_EOF();
else if (atlist != '@' && atlist != '.')
LispUnget(atlist);
comma = LispDoRead(info);
if (comma == DOT) {
atlist = '@';
comma = LispDoRead(info);
}
if (INVALIDP(comma))
READ_ERROR_INVARG();
result = COMMA(comma, atlist == '@' || atlist == '.');
return (result);
}
/*
* Read anything that is not readily identifiable by it's first character
* and also put the code for reading atoms, numbers and strings together.
*/
static LispObj *
LispReadObject(int unintern, read_info *info)
{
READ_ENTER();
LispObj *object;
char stk[128], *string, *package, *symbol;
int ch, length, backslash, size, quote, unreadable, collon;
package = symbol = string = stk;
size = sizeof(stk);
backslash = quote = unreadable = collon = 0;
length = 0;
ch = LispGet();
if (unintern && (ch == ':' || ch == '"'))
READ_ERROR0("syntax error after #:");
else if (ch == '"' || ch == '|')
quote = ch;
else if (ch == '\\') {
unreadable = backslash = 1;
string[length++] = ch;
}
else if (ch == ':') {
collon = 1;
string[length++] = ch;
symbol = string + 1;
ch = LispGet();
if (ch == '|') {
quote = ch;
unreadable = 1;
}
else if (ch != EOF)
LispUnget(ch);
}
else if (ch) {
if (islower(ch))
ch = toupper(ch);
string[length++] = ch;
}
else
unreadable = 1;
/* read remaining data */
for (; ch;) {
ch = LispGet();
if (ch == EOF) {
if (quote) {
/* if quote, file ended with an open quoted object */
if (string != stk)
LispFree(string);
return (NULL);
}
break;
}
else if (ch == '\0')
break;
if (ch == '\\') {
backslash = !backslash;
if (quote == '"') {
/* only remove backslashs from strings */
if (backslash)
continue;
}
else
unreadable = 1;
}
else if (backslash)
backslash = 0;
else if (ch == quote)
break;
else if (!quote && !backslash) {
if (islower(ch))
ch = toupper(ch);
else if (isspace(ch))
break;
else if (AtomSeparator(ch, 0, 0)) {
LispUnget(ch);
break;
}
else if (ch == ':') {
if (collon == 0 ||
(collon == (1 - unintern) && symbol == string + length)) {
++collon;
symbol = string + length + 1;
}
else
READ_ERROR0("too many collons");
}
}
if (length + 2 >= size) {
if (string == stk) {
size = 1024;
string = LispMalloc(size);
strcpy(string, stk);
}
else {
size += 1024;
string = LispRealloc(string, size);
}
symbol = string + (symbol - package);
package = string;
}
string[length++] = ch;
}
if (info->discard) {
if (string != stk)
LispFree(string);
return (ch == EOF ? NULL : NIL);
}
string[length] = '\0';
if (unintern) {
if (length == 0)
READ_ERROR0("syntax error after #:");
object = UNINTERNED_ATOM(string);
}
else if (quote == '"')
object = LSTRING(string, length);
else if (collon) {
/* Package specified in object name */
symbol[-1] = '\0';
if (collon > 1)
symbol[-2] = '\0';
object = LispParseAtom(package, symbol,
collon == 2, unreadable,
read__stream, read__line);
}
else if (quote == '|' || (unreadable && !collon)) {
/* Set unreadable field, this atom needs quoting to be read back */
object = ATOM(string);
object->data.atom->unreadable = 1;
}
/* Check some common symbols */
else if (length == 1 && string[0] == 'T')
/* The T */
object = T;
else if (length == 1 && string[0] == '.')
/* The dot */
object = DOT;
else if (length == 3 &&
string[0] == 'N' && string[1] == 'I' && string[2] == 'L')
/* The NIL */
object = NIL;
else if (isdigit(string[0]) || string[0] == '.' ||
((string[0] == '-' || string[0] == '+') && string[1]))
/* Looks like a number */
object = LispParseNumber(string, 10, read__stream, read__line);
else
/* A normal atom */
object = ATOM(string);
if (string != stk)
LispFree(string);
return (object);
}
static LispObj *
LispParseAtom(char *package, char *symbol, int intern, int unreadable,
LispObj *read__stream, int read__line)
{
LispObj *object = NULL, *thepackage = NULL;
LispPackage *pack = NULL;
if (!unreadable) {
/* Until NIL and T be treated as normal symbols */
if (symbol[0] == 'N' && symbol[1] == 'I' &&
symbol[2] == 'L' && symbol[3] == '\0')
return (NIL);
if (symbol[0] == 'T' && symbol[1] == '\0')
return (T);
unreadable = !LispCheckAtomString(symbol);
}
/* If package is empty, it is a keyword */
if (package[0] == '\0') {
thepackage = lisp__data.keyword;
pack = lisp__data.key;
}
else {
/* Else, search it in the package list */
thepackage = LispFindPackageFromString(package);
if (thepackage == NIL)
READ_ERROR1("the package %s is not available", package);
pack = thepackage->data.package.package;
}
if (pack == lisp__data.pack && intern) {
/* Redundant package specification, since requesting a
* intern symbol, create it if does not exist */
object = ATOM(symbol);
if (unreadable)
object->data.atom->unreadable = 1;
}
else if (intern || pack == lisp__data.key) {
/* Symbol is created, or just fetched from the specified package */
LispPackage *savepack;
LispObj *savepackage = PACKAGE;
/* Remember curent package */
savepack = lisp__data.pack;
/* Temporarily set another package */
lisp__data.pack = pack;
PACKAGE = thepackage;
/* Get the object pointer */
if (pack == lisp__data.key)
object = KEYWORD(LispDoGetAtom(symbol, 0)->key->value);
else
object = ATOM(symbol);
if (unreadable)
object->data.atom->unreadable = 1;
/* Restore current package */
lisp__data.pack = savepack;
PACKAGE = savepackage;
}
else {
/* Symbol must exist (and be extern) in the specified package */
LispAtom *atom;
atom = (LispAtom *)hash_check(pack->atoms, symbol, strlen(symbol));
if (atom)
object = atom->object;
/* No object found */
if (object == NULL || object->data.atom->ext == 0)
READ_ERROR2("no extern symbol %s in package %s", symbol, package);
}
return (object);
}
static LispObj *
LispParseNumber(char *str, int radix, LispObj *read__stream, int read__line)
{
int len;
long integer;
double dfloat;
char *ratio, *ptr;
LispObj *number;
mpi *bignum;
mpr *bigratio;
if (radix < 2 || radix > 36)
READ_ERROR1("radix %d is not in the range 2 to 36", radix);
if (*str == '\0')
return (NULL);
ratio = strchr(str, '/');
if (ratio) {
/* check if looks like a correctly specified ratio */
if (ratio[1] == '\0' || strchr(ratio + 1, '/') != NULL)
return (ATOM(str));
/* ratio must point to an integer in radix base */
*ratio++ = '\0';
}
else if (radix == 10) {
int dot = 0;
int type = 0;
/* check if it is a floating point number */
ptr = str;
if (*ptr == '-' || *ptr == '+')
++ptr;
else if (*ptr == '.') {
dot = 1;
++ptr;
}
while (*ptr) {
if (*ptr == '.') {
if (dot)
return (ATOM(str));
/* ignore it if last char is a dot */
if (ptr[1] == '\0') {
*ptr = '\0';
break;
}
dot = 1;
}
else if (!isdigit(*ptr))
break;
++ptr;
}
switch (*ptr) {
case '\0':
if (dot) /* if dot, it is default float */
type = 'E';
break;
case 'E': case 'S': case 'F': case 'D': case 'L':
type = *ptr;
*ptr = 'E';
break;
default:
return (ATOM(str)); /* syntax error */
}
/* if type set, it is not an integer specification */
if (type) {
if (*ptr) {
int itype = *ptr;
char *ptype = ptr;
++ptr;
if (*ptr == '+' || *ptr == '-')
++ptr;
while (*ptr && isdigit(*ptr))
++ptr;
if (*ptr) {
*ptype = itype;
return (ATOM(str));
}
}
dfloat = strtod(str, NULL);
if (!finite(dfloat))
READ_ERROR0("floating point overflow");
return (DFLOAT(dfloat));
}
}
/* check if correctly specified in the given radix */
len = strlen(str) - 1;
if (!ratio && radix != 10 && str[len] == '.')
str[len] = '\0';
if (ratio || radix != 10) {
if (!StringInRadix(str, radix, 1)) {
if (ratio)
ratio[-1] = '/';
return (ATOM(str));
}
if (ratio && !StringInRadix(ratio, radix, 0)) {
ratio[-1] = '/';
return (ATOM(str));
}
}
bignum = NULL;
bigratio = NULL;
errno = 0;
integer = strtol(str, NULL, radix);
/* if does not fit in a long */
if (errno == ERANGE) {
bignum = LispMalloc(sizeof(mpi));
mpi_init(bignum);
mpi_setstr(bignum, str, radix);
}
if (ratio && integer != 0) {
long denominator;
errno = 0;
denominator = strtol(ratio, NULL, radix);
if (denominator == 0)
READ_ERROR0("divide by zero");
if (bignum == NULL) {
if (integer == MINSLONG ||
(denominator == LONG_MAX && errno == ERANGE)) {
bigratio = LispMalloc(sizeof(mpr));
mpr_init(bigratio);
mpi_seti(mpr_num(bigratio), integer);
mpi_setstr(mpr_den(bigratio), ratio, radix);
}
}
else {
bigratio = LispMalloc(sizeof(mpr));
mpr_init(bigratio);
mpi_set(mpr_num(bigratio), bignum);
mpi_clear(bignum);
LispFree(bignum);
mpi_setstr(mpr_den(bigratio), ratio, radix);
}
if (bigratio) {
mpr_canonicalize(bigratio);
if (mpi_fiti(mpr_num(bigratio)) &&
mpi_fiti(mpr_den(bigratio))) {
integer = mpi_geti(mpr_num(bigratio));
denominator = mpi_geti(mpr_den(bigratio));
mpr_clear(bigratio);
LispFree(bigratio);
if (denominator == 1)
number = INTEGER(integer);
else
number = RATIO(integer, denominator);
}
else
number = BIGRATIO(bigratio);
}
else {
long num = integer, den = denominator, rest;
if (num < 0)
num = -num;
for (;;) {
if ((rest = den % num) == 0)
break;
den = num;
num = rest;
}
if (den != 1) {
denominator /= num;
integer /= num;
}
if (denominator < 0) {
integer = -integer;
denominator = -denominator;
}
if (denominator == 1)
number = INTEGER(integer);
else
number = RATIO(integer, denominator);
}
}
else if (bignum)
number = BIGNUM(bignum);
else
number = INTEGER(integer);
return (number);
}
static int
StringInRadix(char *str, int radix, int skip_sign)
{
if (skip_sign && (*str == '-' || *str == '+'))
++str;
while (*str) {
if (*str >= '0' && *str <= '9') {
if (*str - '0' >= radix)
return (0);
}
else if (*str >= 'A' && *str <= 'Z') {
if (radix <= 10 || *str - 'A' + 10 >= radix)
return (0);
}
else
return (0);
str++;
}
return (1);
}
static int
AtomSeparator(int ch, int check_space, int check_backslash)
{
if (check_space && isspace(ch))
return (1);
if (check_backslash && ch == '\\')
return (1);
return (strchr("(),\";'`#|,", ch) != NULL);
}
static LispObj *
LispReadVector(read_info *info)
{
LispObj *objects;
int nodot = info->nodot;
info->nodot = info->level + 1;
objects = LispReadList(info);
info->nodot = nodot;
if (info->discard)
return (objects);
return (VECTOR(objects));
}
static LispObj *
LispReadFunction(read_info *info)
{
READ_ENTER();
int nodot = info->nodot;
LispObj *function;
info->nodot = info->level + 1;
function = LispDoRead(info);
info->nodot = nodot;
if (info->discard)
return (function);
if (INVALIDP(function))
READ_ERROR_INVARG();
else if (CONSP(function)) {
if (CAR(function) != Olambda)
READ_ERROR_INVARG();
return (FUNCTION_QUOTE(function));
}
else if (!SYMBOLP(function))
READ_ERROR_INVARG();
return (FUNCTION_QUOTE(function));
}
static LispObj *
LispReadRational(int radix, read_info *info)
{
READ_ENTER();
LispObj *number;
int ch, len, size;
char stk[128], *str;
len = 0;
str = stk;
size = sizeof(stk);
for (;;) {
ch = LispGet();
if (ch == EOF || isspace(ch))
break;
else if (AtomSeparator(ch, 0, 1)) {
LispUnget(ch);
break;
}
else if (islower(ch))
ch = toupper(ch);
if ((ch < '0' || ch > '9') && (ch < 'A' || ch > 'Z') &&
ch != '+' && ch != '-' && ch != '/') {
if (str != stk)
LispFree(str);
if (!info->discard)
READ_ERROR1("bad character %c for rational number", ch);
}
if (len + 1 >= size) {
if (str == stk) {
size = 512;
str = LispMalloc(size);
strcpy(str + 1, stk + 1);
}
else {
size += 512;
str = LispRealloc(str, size);
}
}
str[len++] = ch;
}
if (info->discard) {
if (str != stk)
LispFree(str);
return (ch == EOF ? NULL : NIL);
}
str[len] = '\0';
number = LispParseNumber(str, radix, read__stream, read__line);
if (str != stk)
LispFree(str);
if (!RATIONALP(number))
READ_ERROR0("bad rational number specification");
return (number);
}
static LispObj *
LispReadCharacter(read_info *info)
{
READ_ENTER();
long c;
int ch, len;
char stk[64];
ch = LispGet();
if (ch == EOF)
return (NULL);
stk[0] = ch;
len = 1;
for (;;) {
ch = LispGet();
if (ch == EOF)
break;
else if (ch != '-' && !isalnum(ch)) {
LispUnget(ch);
break;
}
if (len + 1 < sizeof(stk))
stk[len++] = ch;
}
if (len > 1) {
const char * const *names;
int found = 0;
stk[len] = '\0';
for (c = ch = 0; ch <= ' ' && !found; ch++) {
for (names = LispChars[ch].names; *names; names++)
if (strcasecmp(*names, stk) == 0) {
c = ch;
found = 1;
break;
}
}
if (!found) {
for (names = LispChars[0177].names; *names; names++)
if (strcasecmp(*names, stk) == 0) {
c = 0177;
found = 1;
break;
}
}
if (!found) {
if (info->discard)
return (NIL);
READ_ERROR1("unkwnown character %s", stk);
}
}
else
c = stk[0];
return (SCHAR(c));
}
static void
LispSkipComment(void)
{
READ_ENTER();
int ch, comm = 1;
for (;;) {
ch = LispGet();
if (ch == '#') {
ch = LispGet();
if (ch == '|')
++comm;
continue;
}
while (ch == '|') {
ch = LispGet();
if (ch == '#' && --comm == 0)
return;
}
if (ch == EOF)
READ_ERROR_EOF();
}
}
static LispObj *
LispReadEval(read_info *info)
{
READ_ENTER();
int nodot = info->nodot;
LispObj *code;
info->nodot = info->level + 1;
code = LispDoRead(info);
info->nodot = nodot;
if (info->discard)
return (code);
if (INVALIDP(code))
READ_ERROR_INVARG();
return (EVAL(code));
}
static LispObj *
LispReadComplex(read_info *info)
{
READ_ENTER();
GC_ENTER();
int nodot = info->nodot;
LispObj *number, *arguments;
info->nodot = info->level + 1;
arguments = LispDoRead(info);
info->nodot = nodot;
/* form read */
if (info->discard)
return (arguments);
if (INVALIDP(arguments) || !CONSP(arguments))
READ_ERROR_INVARG();
GC_PROTECT(arguments);
number = APPLY(Ocomplex, arguments);
GC_LEAVE();
return (number);
}
static LispObj *
LispReadPathname(read_info *info)
{
READ_ENTER();
GC_ENTER();
int nodot = info->nodot;
LispObj *path, *arguments;
info->nodot = info->level + 1;
arguments = LispDoRead(info);
info->nodot = nodot;
/* form read */
if (info->discard)
return (arguments);
if (INVALIDP(arguments))
READ_ERROR_INVARG();
GC_PROTECT(arguments);
path = APPLY1(Oparse_namestring, arguments);
GC_LEAVE();
return (path);
}
static LispObj *
LispReadStruct(read_info *info)
{
READ_ENTER();
GC_ENTER();
int len, nodot = info->nodot;
char stk[128], *str;
LispObj *struc, *fields;
info->nodot = info->level + 1;
fields = LispDoRead(info);
info->nodot = nodot;
/* form read */
if (info->discard)
return (fields);
if (INVALIDP(fields) || !CONSP(fields) || !SYMBOLP(CAR(fields)))
READ_ERROR_INVARG();
GC_PROTECT(fields);
len = ATOMID(CAR(fields))->length;
/* MAKE- */
if (len + 6 > sizeof(stk))
str = LispMalloc(len + 6);
else
str = stk;
sprintf(str, "MAKE-%s", ATOMID(CAR(fields))->value);
RPLACA(fields, ATOM(str));
if (str != stk)
LispFree(str);
struc = APPLY(Omake_struct, fields);
GC_LEAVE();
return (struc);
}
/* XXX This is broken, needs a rewritten as soon as true vector/arrays be
* implemented. */
static LispObj *
LispReadArray(long dimensions, read_info *info)
{
READ_ENTER();
GC_ENTER();
long count;
int nodot = info->nodot;
LispObj *arguments, *initial, *dim, *cons, *array, *data;
info->nodot = info->level + 1;
data = LispDoRead(info);
info->nodot = nodot;
/* form read */
if (info->discard)
return (data);
if (INVALIDP(data))
READ_ERROR_INVARG();
initial = Kinitial_contents;
dim = cons = NIL;
if (dimensions) {
LispObj *array;
for (count = 0, array = data; count < dimensions; count++) {
long length;
LispObj *item;
if (!CONSP(array))
READ_ERROR0("bad array for given dimension");
item = array;
array = CAR(array);
for (length = 0; CONSP(item); item = CDR(item), length++)
;
if (dim == NIL) {
dim = cons = CONS(FIXNUM(length), NIL);
GC_PROTECT(dim);
}
else {
RPLACD(cons, CONS(FIXNUM(length), NIL));
cons = CDR(cons);
}
}
}
arguments = CONS(dim, CONS(initial, CONS(data, NIL)));
GC_PROTECT(arguments);
array = APPLY(Omake_array, arguments);
GC_LEAVE();
return (array);
}
static LispObj *
LispReadFeature(int with, read_info *info)
{
READ_ENTER();
LispObj *status;
LispObj *feature = LispDoRead(info);
/* form read */
if (info->discard)
return (feature);
if (INVALIDP(feature))
READ_ERROR_INVARG();
/* paranoia check, features must be a list, possibly empty */
if (!CONSP(FEATURES) && FEATURES != NIL)
READ_ERROR1("%s is not a list", STROBJ(FEATURES));
status = LispEvalFeature(feature);
if (with) {
if (status == T)
return (LispDoRead(info));
/* need to use the field discard because the following expression
* may be #.FORM or #,FORM or any other form that may generate
* side effects */
info->discard = 1;
LispDoRead(info);
info->discard = 0;
return (LispDoRead(info));
}
if (status == NIL)
return (LispDoRead(info));
info->discard = 1;
LispDoRead(info);
info->discard = 0;
return (LispDoRead(info));
}
/*
* A very simple eval loop with AND, NOT, and OR functions for testing
* the available features.
*/
static LispObj *
LispEvalFeature(LispObj *feature)
{
READ_ENTER();
Atom_id test;
LispObj *object;
if (CONSP(feature)) {
LispObj *function = CAR(feature), *arguments = CDR(feature);
if (!SYMBOLP(function))
READ_ERROR1("bad feature test function %s", STROBJ(function));
if (!CONSP(arguments))
READ_ERROR1("bad feature test arguments %s", STROBJ(arguments));
test = ATOMID(function);
if (test == Sand) {
for (; CONSP(arguments); arguments = CDR(arguments)) {
if (LispEvalFeature(CAR(arguments)) == NIL)
return (NIL);
}
return (T);
}
else if (test == Sor) {
for (; CONSP(arguments); arguments = CDR(arguments)) {
if (LispEvalFeature(CAR(arguments)) == T)
return (T);
}
return (NIL);
}
else if (test == Snot) {
if (CONSP(CDR(arguments)))
READ_ERROR0("too many arguments to NOT");
return (LispEvalFeature(CAR(arguments)) == NIL ? T : NIL);
}
else
READ_ERROR1("unimplemented feature test function %s", test);
}
if (KEYWORDP(feature))
feature = feature->data.quote;
else if (!SYMBOLP(feature))
READ_ERROR1("bad feature specification %s", STROBJ(feature));
test = ATOMID(feature);
for (object = FEATURES; CONSP(object); object = CDR(object)) {
/* paranoia check, elements in the feature list must ge keywords */
if (!KEYWORDP(CAR(object)))
READ_ERROR1("%s is not a keyword", STROBJ(CAR(object)));
if (ATOMID(CAR(object)) == test)
return (T);
}
/* unknown feature */
return (NIL);
}