2056 lines
47 KiB
C
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);
|
|
}
|