862 lines
22 KiB
C
862 lines
22 KiB
C
|
/* lysp.c -- LYSP: commemorating 50 Years of Symbolic Processing
|
||
|
** (no relation to the long-deceased dialect from IBM)
|
||
|
**
|
||
|
** Copyright (c) 2008 Ian Piumarta
|
||
|
** All Rights Reserved
|
||
|
**
|
||
|
** 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, and/or sell
|
||
|
** copies of the Software, and to permit persons to whom the Software is
|
||
|
** furnished to do so, provided that the above copyright notice(s) and this
|
||
|
** permission notice appear in all copies of the Software. Inclusion of the
|
||
|
** the above copyright notice(s) and this permission notice in supporting
|
||
|
** documentation would be appreciated but is not required.
|
||
|
**
|
||
|
** THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
|
||
|
**
|
||
|
** Last edited: 2012-01-12 15:44:18 by piumarta on vps2.piumarta.com
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <ctype.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include <sys/errno.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#ifndef BDWGC
|
||
|
# error you did neither -DBDWGC=0 nor -DBDWGC=1 in the compilation command
|
||
|
#endif
|
||
|
|
||
|
#if BDWGC
|
||
|
# include <gc/gc.h>
|
||
|
# define GC_PROTECT(X)
|
||
|
# define GC_UNPROTECT(X)
|
||
|
#else
|
||
|
# include "gc.h"
|
||
|
#endif
|
||
|
#define balloc GC_malloc_atomic
|
||
|
#define malloc GC_malloc
|
||
|
|
||
|
static void fatal(const char *fmt, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
va_start(ap, fmt);
|
||
|
fprintf(stderr, "\nError: ");
|
||
|
vfprintf(stderr, fmt, ap);
|
||
|
fprintf(stderr, "\n");
|
||
|
va_end(ap);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
typedef enum { None, Number, String, Symbol, Cons, Subr, Fsubr, Expr, Fexpr, Psubr } Tag;
|
||
|
|
||
|
struct _Cell;
|
||
|
typedef struct _Cell Cell;
|
||
|
|
||
|
typedef Cell *(*Subr_t)(Cell *args, Cell *env);
|
||
|
|
||
|
Cell *apply(Cell *fn, Cell *args, Cell *env);
|
||
|
|
||
|
struct _Cell
|
||
|
{
|
||
|
Tag mTag;
|
||
|
union {
|
||
|
long mNumber;
|
||
|
const char *mString;
|
||
|
const char *mSymbol;
|
||
|
struct {
|
||
|
Cell *a;
|
||
|
Cell *d;
|
||
|
} mCons;
|
||
|
Subr_t mSubr;
|
||
|
struct {
|
||
|
Cell *expr;
|
||
|
Cell *env;
|
||
|
} mExpr;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
Cell *mkNumber(long n) { Cell *self= balloc(sizeof(Cell)); self->mTag= Number; self->mNumber= n; return self; }
|
||
|
Cell *mkString(const char *s) { Cell *self= balloc(sizeof(Cell)); self->mTag= String; self->mString= s; return self; }
|
||
|
Cell *mkSymbol(const char *s) { Cell *self= balloc(sizeof(Cell)); self->mTag= Symbol; self->mString= s; return self; }
|
||
|
Cell *cons(Cell *a, Cell *d) { Cell *self= malloc(sizeof(Cell)); self->mTag= Cons; self->mCons.a= a; self->mCons.d= d; return self; }
|
||
|
Cell *mkSubr(Subr_t fn) { Cell *self= balloc(sizeof(Cell)); self->mTag= Subr; self->mSubr= fn; return self; }
|
||
|
Cell *mkFsubr(Subr_t fn) { Cell *self= balloc(sizeof(Cell)); self->mTag= Fsubr; self->mSubr= fn; return self; }
|
||
|
Cell *mkExpr(Cell *x, Cell *e) { Cell *self= malloc(sizeof(Cell)); self->mTag= Expr; self->mExpr.expr= x; self->mExpr.env= e; return self; }
|
||
|
Cell *mkFexpr(Cell *x, Cell *e) { Cell *self= malloc(sizeof(Cell)); self->mTag= Fexpr; self->mExpr.expr= x; self->mExpr.env= e; return self; }
|
||
|
Cell *mkPsubr(Subr_t fn) { Cell *self= balloc(sizeof(Cell)); self->mTag= Psubr; self->mSubr= fn; return self; }
|
||
|
|
||
|
int nilP(Cell *self) { return !self; }
|
||
|
int numberP(Cell *self) { return self && self->mTag == Number; }
|
||
|
int stringP(Cell *self) { return self && self->mTag == String; }
|
||
|
int symbolP(Cell *self) { return self && self->mTag == Symbol; }
|
||
|
int consP(Cell *self) { return self && self->mTag == Cons; }
|
||
|
int subrP(Cell *self) { return self && self->mTag == Subr; }
|
||
|
int fsubrP(Cell *self) { return self && self->mTag == Fsubr; }
|
||
|
int exprP(Cell *self) { return self && self->mTag == Expr; }
|
||
|
int fexprP(Cell *self) { return self && self->mTag == Fexpr; }
|
||
|
int psubrP(Cell *self) { return self && self->mTag == Psubr; }
|
||
|
|
||
|
#ifndef NDEBUG
|
||
|
# define require(X) assert(X)
|
||
|
#else
|
||
|
# define require(X) if (!(X)) return 0
|
||
|
#endif
|
||
|
|
||
|
long number(Cell *self) { require(numberP(self)); return self->mNumber; }
|
||
|
const char *string(Cell *self) { require(stringP(self)); return self->mString; }
|
||
|
const char *symbol(Cell *self) { require(symbolP(self)); return self->mSymbol; }
|
||
|
Subr_t subr(Cell *self) { require(subrP(self)); return self->mSubr; }
|
||
|
Subr_t fsubr(Cell *self) { require(fsubrP(self)); return self->mSubr; }
|
||
|
Cell *expr(Cell *self) { require(exprP(self)); return self->mExpr.expr; }
|
||
|
Cell *exprenv(Cell *self) { require(exprP(self)); return self->mExpr.env; }
|
||
|
Cell *fexpr(Cell *self) { require(fexprP(self)); return self->mExpr.expr; }
|
||
|
Cell *fexprenv(Cell *self) { require(fexprP(self)); return self->mExpr.env; }
|
||
|
Subr_t psubr(Cell *self) { require(psubrP(self)); return self->mSubr; }
|
||
|
|
||
|
Cell *car(Cell *self) { require(!self || consP(self)); return self ? self->mCons.a : 0; }
|
||
|
Cell *cdr(Cell *self) { require(!self || consP(self)); return self ? self->mCons.d : 0; }
|
||
|
Cell *rplaca(Cell *self, Cell *c) { require(!self || consP(self)); return self ? self->mCons.a= c : c; }
|
||
|
Cell *rplacd(Cell *self, Cell *c) { require(!self || consP(self)); return self ? self->mCons.d= c : c; }
|
||
|
|
||
|
#undef require
|
||
|
|
||
|
Cell *caar (Cell *self) { return car(car(self)); }
|
||
|
Cell *cadr (Cell *self) { return car(cdr(self)); }
|
||
|
Cell *cdar (Cell *self) { return cdr(car(self)); }
|
||
|
Cell *caddr (Cell *self) { return car(cdr(cdr(self))); }
|
||
|
Cell *cadar (Cell *self) { return car(cdr(car(self))); }
|
||
|
|
||
|
Cell *interns= 0;
|
||
|
|
||
|
Cell *intern(const char *s)
|
||
|
{
|
||
|
Cell *cell= 0;
|
||
|
for (cell= interns; cell; cell= cdr(cell))
|
||
|
if (!strcmp(symbol(car(cell)), s))
|
||
|
return car(cell);
|
||
|
cell= mkSymbol(strdup(s));
|
||
|
GC_PROTECT(cell);
|
||
|
interns= cons(cell, interns);
|
||
|
GC_UNPROTECT(cell);
|
||
|
return car(interns);
|
||
|
}
|
||
|
|
||
|
Cell *assq(Cell *key, Cell *list)
|
||
|
{
|
||
|
for (; list; list= cdr(list))
|
||
|
if (key == caar(list)) return car(list);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *print(Cell *self, FILE *stream)
|
||
|
{
|
||
|
if (!self) fprintf(stream, "nil");
|
||
|
else
|
||
|
switch (self->mTag) {
|
||
|
case Number: fprintf(stream, "%ld", number(self)); break;
|
||
|
case String: fprintf(stream, "\"%s\"", string(self)); break;
|
||
|
case Symbol: fprintf(stream, "%s", symbol(self)); break;
|
||
|
case Subr: fprintf(stream, "subr<%p>", subr(self)); break;
|
||
|
case Fsubr: fprintf(stream, "fsubr<%p>", fsubr(self)); break;
|
||
|
case Expr: fprintf(stream, "(lambda "); print(expr(self), stream); fprintf(stream, ")"); break;
|
||
|
case Fexpr: fprintf(stream, "(flambda "); print(fexpr(self), stream); fprintf(stream, ")"); break;
|
||
|
case Psubr: fprintf(stream, "psubr<%p>", psubr(self)); break;
|
||
|
case Cons: {
|
||
|
fprintf(stream, "(");
|
||
|
while (self && consP(self)) {
|
||
|
print(car(self), stream);
|
||
|
if ((self= cdr(self))) fputc(' ', stream);
|
||
|
}
|
||
|
if (self) {
|
||
|
fprintf(stream, ". ");
|
||
|
print(self, stream);
|
||
|
}
|
||
|
fprintf(stream, ")");
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
fprintf(stream, "?%p", self);
|
||
|
break;
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
Cell *println(Cell *self, FILE *stream)
|
||
|
{
|
||
|
print(self, stream);
|
||
|
fprintf(stream, "\n");
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
Cell *_S_t = 0;
|
||
|
Cell *_S_quote = 0;
|
||
|
Cell *_S_qquote = 0;
|
||
|
Cell *_S_uquote = 0;
|
||
|
Cell *_S_uquotes = 0;
|
||
|
Cell *syntaxTable= 0;
|
||
|
Cell *globals= 0;
|
||
|
|
||
|
typedef Cell *(*Reader)(int, FILE *);
|
||
|
|
||
|
Cell *readFile(FILE *in);
|
||
|
Cell *readAlpha(int c, FILE *in);
|
||
|
Cell *readSign(int c, FILE *in);
|
||
|
|
||
|
Reader readers[256];
|
||
|
|
||
|
#define CEOF ((Cell *)-1)
|
||
|
|
||
|
Cell *readIllegal(int c, FILE *in)
|
||
|
{
|
||
|
fprintf(stderr, "ignoring illegal character ");
|
||
|
fprintf(stderr, (isprint(c) ? "%c" : "0x%02x"), c);
|
||
|
fprintf(stderr, "\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *readBlank(int c, FILE *in)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *readDigit(int c, FILE *in)
|
||
|
{
|
||
|
char buf[1024];
|
||
|
int index= 0;
|
||
|
char *endptr;
|
||
|
long number= 0;
|
||
|
buf[index++]= c;
|
||
|
if ('0' == c) {
|
||
|
if (strchr("xX", (c= getc(in)))) buf[index++]= c;
|
||
|
else ungetc(c, in);
|
||
|
}
|
||
|
while ((c= getc(in)) > 0 && (readDigit == readers[c] || readAlpha == readers[c])) buf[index++]= c;
|
||
|
ungetc(c, in);
|
||
|
buf[index]= '\0';
|
||
|
errno= 0;
|
||
|
number= strtol(buf, &endptr, 0);
|
||
|
if ((ERANGE == errno) || (errno && !number)) perror(buf);
|
||
|
if (*endptr != '\0') fprintf(stderr, "%s: invalid digits in number\n", buf);
|
||
|
return mkNumber(number);
|
||
|
}
|
||
|
|
||
|
Cell *readAlpha(int c, FILE *in)
|
||
|
{
|
||
|
char buf[1024];
|
||
|
int index= 0;
|
||
|
buf[index++]= c;
|
||
|
while ((c= getc(in)) > 0 && (readAlpha == readers[c] || readDigit == readers[c] || readSign == readers[c])) buf[index++]= c;
|
||
|
ungetc(c, in);
|
||
|
buf[index]= '\0';
|
||
|
return intern(buf);
|
||
|
}
|
||
|
|
||
|
Cell *readSign(int c, FILE *in)
|
||
|
{
|
||
|
int d= getc(in);
|
||
|
ungetc(d, in);
|
||
|
return (d > 0 && readers[d] == readDigit) ? readDigit(c, in) : readAlpha(c, in);
|
||
|
}
|
||
|
|
||
|
Cell *readString(int d, FILE *in)
|
||
|
{
|
||
|
char buf[1024];
|
||
|
int index= 0;
|
||
|
int c;
|
||
|
while ((c= getc(in)) > 0 && c != d) if ('\\' == (buf[index++]= c)) buf[index++]= getc(in);
|
||
|
if (c != d) fatal("EOF in string");
|
||
|
buf[index]= '\0';
|
||
|
return mkString(strdup(buf));
|
||
|
}
|
||
|
|
||
|
Cell *readQuote(int c, FILE *in)
|
||
|
{
|
||
|
Cell *cell= readFile(in);
|
||
|
if (CEOF == cell) fatal("EOF in quoted literal");
|
||
|
GC_PROTECT(cell);
|
||
|
cell= cons(cell, 0);
|
||
|
cell= cons(_S_quote, cell);
|
||
|
GC_UNPROTECT(cell);
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
Cell *readQquote(int c, FILE *in)
|
||
|
{
|
||
|
Cell *cell= readFile(in);
|
||
|
if (CEOF == cell) fatal("EOF in quasiquoted literal");
|
||
|
GC_PROTECT(cell);
|
||
|
cell= cons(cell, 0);
|
||
|
cell= cons(_S_qquote, cell);
|
||
|
GC_UNPROTECT(cell);
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
Cell *readUquote(int c, FILE *in)
|
||
|
{
|
||
|
int splice= 0;
|
||
|
if ('@' == (c= getc(in))) splice= 1;
|
||
|
else ungetc(c, in);
|
||
|
Cell *cell= readFile(in);
|
||
|
if (CEOF == cell) fatal("EOF in quasiquoted literal");
|
||
|
GC_PROTECT(cell);
|
||
|
cell= cons(cell, 0);
|
||
|
cell= cons((splice ? _S_uquotes : _S_uquote), cell);
|
||
|
GC_UNPROTECT(cell);
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
Cell *readList(int d, FILE *in)
|
||
|
{
|
||
|
Cell *head, *tail, *cell= 0;
|
||
|
tail= head= cons(0, 0);
|
||
|
GC_PROTECT(head);
|
||
|
GC_PROTECT(cell);
|
||
|
switch (d) {
|
||
|
case '(': d= ')'; break;
|
||
|
case '[': d= ']'; break;
|
||
|
case '{': d= '}'; break;
|
||
|
}
|
||
|
int c;
|
||
|
for (;;) {
|
||
|
while (isspace((c= getc(in))));
|
||
|
if (c == d) break;
|
||
|
if (c == ')' || c == ']' || c == '}') fatal("mismatched parentheses");
|
||
|
if (c == '.')
|
||
|
rplacd(tail, readFile(in));
|
||
|
else {
|
||
|
ungetc(c, in);
|
||
|
cell= readFile(in);
|
||
|
if (feof(in)) fatal("EOF in list");
|
||
|
tail= rplacd(tail, cons(cell, 0));
|
||
|
}
|
||
|
}
|
||
|
head= cdr(head);
|
||
|
if (head && symbolP(car(head))) {
|
||
|
Cell *syntax= assq(car(head), cdr(syntaxTable));
|
||
|
if (syntax) head= apply(cdr(syntax), cdr(head), globals);
|
||
|
if (!head) {
|
||
|
GC_UNPROTECT(head);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
GC_UNPROTECT(head);
|
||
|
return head ? head : (Cell *)-1;
|
||
|
}
|
||
|
|
||
|
Cell *readSemi(int c, FILE *in)
|
||
|
{
|
||
|
while ((c= getc(in)) && (c != '\n') && (c != '\r'));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *readFile(FILE *in)
|
||
|
{
|
||
|
int c;
|
||
|
Cell *cell;
|
||
|
do {
|
||
|
while (isspace(c= getc(in)));
|
||
|
if (c < 0) return (Cell *)-1;
|
||
|
cell= readers[c](c, in);
|
||
|
} while (!cell);
|
||
|
if (cell == (Cell *)-1) cell= 0;
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
static void initReaders(Reader r, const char *chars)
|
||
|
{
|
||
|
while (*chars) readers[(int)*chars++]= r;
|
||
|
}
|
||
|
|
||
|
Cell *undefined(Cell *sym)
|
||
|
{
|
||
|
fprintf(stderr, "undefined: %s\n", symbol(sym));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *eval(Cell *expr, Cell *env);
|
||
|
|
||
|
Cell *evargs(Cell *self, Cell *env)
|
||
|
{
|
||
|
if (self) {
|
||
|
Cell *head, *tail;
|
||
|
head= eval(car(self), env);
|
||
|
GC_PROTECT(head);
|
||
|
tail= evargs(cdr(self), env);
|
||
|
GC_PROTECT(tail);
|
||
|
head= cons(head, tail);
|
||
|
GC_UNPROTECT(head);
|
||
|
return head;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *evbind(Cell *expr, Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *cell= 0;
|
||
|
GC_PROTECT(env);
|
||
|
GC_PROTECT(cell);
|
||
|
if (consP(expr))
|
||
|
for (; expr; expr= cdr(expr), args= cdr(args)) {
|
||
|
cell= cons(car(expr), car(args));
|
||
|
env= cons(cell, env);
|
||
|
}
|
||
|
else {
|
||
|
cell= cons(expr, args);
|
||
|
env= cons(cell, env);
|
||
|
}
|
||
|
GC_UNPROTECT(env);
|
||
|
return env;
|
||
|
}
|
||
|
|
||
|
Cell *evlist(Cell *expr, Cell *env)
|
||
|
{
|
||
|
Cell *result= 0;
|
||
|
GC_PROTECT(expr);
|
||
|
GC_PROTECT(env);
|
||
|
GC_PROTECT(result);
|
||
|
for (; expr; expr= cdr(expr)) result= eval(car(expr), env);
|
||
|
GC_UNPROTECT(expr);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
typedef void (*apply_t)(void);
|
||
|
typedef union { char *argp; } *arglist_t;
|
||
|
|
||
|
long primcall(apply_t fn, arglist_t args, int size)
|
||
|
{
|
||
|
void *ret= __builtin_apply(fn, args, size);
|
||
|
__builtin_return(ret);
|
||
|
}
|
||
|
|
||
|
void *cellToPrim(Cell *cell)
|
||
|
{
|
||
|
switch (cell->mTag) {
|
||
|
case Cons: case Expr: case Fexpr: return (void *)cell;
|
||
|
default: return (void *)cell->mCons.a;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Cell *apply(Cell *fn, Cell *args, Cell *env)
|
||
|
{
|
||
|
GC_PROTECT(fn);
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
if (fn)
|
||
|
switch (fn->mTag) {
|
||
|
case Subr: return subr(fn)(evargs(args, env), env);
|
||
|
case Fsubr: return fsubr(fn)(args, env);
|
||
|
case Expr: {
|
||
|
Cell *eva= evargs(args, env);
|
||
|
GC_PROTECT(eva);
|
||
|
eva= evlist(cdr(expr(fn)), evbind(car(expr(fn)), eva, exprenv(fn)));
|
||
|
GC_UNPROTECT(fn);
|
||
|
return eva;
|
||
|
}
|
||
|
case Fexpr: {
|
||
|
Cell *eva= cons(env, 0);
|
||
|
GC_PROTECT(eva);
|
||
|
eva= cons(args, eva);
|
||
|
eva= evlist(cdr(fexpr(fn)), evbind(car(fexpr(fn)), eva, fexprenv(fn)));
|
||
|
GC_UNPROTECT(fn);
|
||
|
return eva;
|
||
|
}
|
||
|
case Psubr: {
|
||
|
void *argv[32]; /* fixme: count the args, then alloca() */
|
||
|
int i;
|
||
|
for (i= 1; args; args= cdr(args), ++i) argv[i]= cellToPrim(eval(car(args), env));
|
||
|
argv[0]= &argv[1];
|
||
|
return mkNumber(primcall((apply_t)psubr(fn), (void*)argv, sizeof(void *) * i));
|
||
|
}
|
||
|
default: break;
|
||
|
}
|
||
|
fprintf(stderr, "cannot apply: ");
|
||
|
println(fn, stderr);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *eval(Cell *expr, Cell *env)
|
||
|
{
|
||
|
if (!expr) return 0;
|
||
|
switch (expr->mTag) {
|
||
|
case Number: case String: case Subr: case Fsubr: case Expr: {
|
||
|
return expr;
|
||
|
}
|
||
|
case Symbol: {
|
||
|
Cell *cell= assq(expr, env);
|
||
|
if (!cell) return undefined(expr);
|
||
|
return cdr(cell);
|
||
|
}
|
||
|
case Cons: {
|
||
|
Cell *cell;
|
||
|
GC_PROTECT(expr);
|
||
|
GC_PROTECT(env);
|
||
|
cell= eval(car(expr), env);
|
||
|
GC_PROTECT(cell);
|
||
|
cell= apply(cell, cdr(expr), env);
|
||
|
GC_UNPROTECT(expr);
|
||
|
return cell;
|
||
|
}
|
||
|
default:
|
||
|
fatal("unknown tag");
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Cell *defineFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *cell= 0;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
if (args) {
|
||
|
cell= cons(car(args), 0);
|
||
|
GC_PROTECT(cell);
|
||
|
rplacd(globals, cons(cell, cdr(globals)));
|
||
|
cell= rplacd(cell, eval(cadr(args), env));
|
||
|
}
|
||
|
GC_UNPROTECT(args);
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
Cell *setqFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *key, *value= 0;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
key= car(args);
|
||
|
if (symbolP(key)) {
|
||
|
Cell *cell;
|
||
|
value= eval(cadr(args), env);
|
||
|
GC_PROTECT(value);
|
||
|
cell= assq(key, env);
|
||
|
if (!cell) {
|
||
|
GC_UNPROTECT(args);
|
||
|
return undefined(key);
|
||
|
}
|
||
|
rplacd(cell, value);
|
||
|
}
|
||
|
GC_UNPROTECT(args);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
Cell *lambdaFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
return mkExpr(args, env);
|
||
|
}
|
||
|
|
||
|
Cell *flambdaFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
return mkFexpr(args, env);
|
||
|
}
|
||
|
|
||
|
Cell *letFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *cell, *tmp= 0;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
GC_PROTECT(tmp);
|
||
|
for (cell= car(args); cell; cell= cdr(cell)) {
|
||
|
tmp= eval(cadar(cell), env);
|
||
|
tmp= cons(caar(cell), tmp);
|
||
|
env= cons(tmp, env);
|
||
|
}
|
||
|
GC_UNPROTECT(args);
|
||
|
return evlist(cdr(args), env);
|
||
|
}
|
||
|
|
||
|
Cell *orFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *value;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
for (value= 0; args && !value; args= cdr(args))
|
||
|
value= eval(car(args), env);
|
||
|
GC_UNPROTECT(args);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
Cell *andFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *value;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
for (value= _S_t; args && value; args= cdr(args))
|
||
|
value= eval(car(args), env);
|
||
|
GC_UNPROTECT(args);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
Cell *ifFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *cell;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
cell= (eval(car(args), env) ? eval(cadr (args), env) : eval(caddr(args), env));
|
||
|
GC_UNPROTECT(args);
|
||
|
return cell;
|
||
|
}
|
||
|
|
||
|
Cell *whileFsubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *result= 0;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
GC_PROTECT(result);
|
||
|
while (eval(car(args), env)) result= evlist(cdr(args), env);
|
||
|
GC_UNPROTECT(args);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Cell *mapArgs(Cell *args)
|
||
|
{
|
||
|
Cell *arg, *tail;
|
||
|
if (!args) return 0;
|
||
|
arg= caar(args);
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(arg);
|
||
|
rplaca(args, cdar(args));
|
||
|
tail= mapArgs(cdr(args));
|
||
|
arg= cons(arg, tail);
|
||
|
GC_UNPROTECT(args);
|
||
|
return arg;
|
||
|
}
|
||
|
|
||
|
Cell *mapSubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *fn= car(args), *head, *tail, *cell= 0;
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
GC_PROTECT(cell);
|
||
|
tail= head= cons(0, 0);
|
||
|
args= cdr(args);
|
||
|
while (car(args)) {
|
||
|
cell= mapArgs(args);
|
||
|
cell= apply(fn, cell, env);
|
||
|
tail= rplacd(tail, cons(cell, 0));
|
||
|
}
|
||
|
GC_UNPROTECT(args);
|
||
|
return cdr(head);
|
||
|
}
|
||
|
|
||
|
Cell *evalSubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
Cell *evalArg= car(args);
|
||
|
Cell *evalEnv= cadr(args);
|
||
|
GC_PROTECT(args);
|
||
|
GC_PROTECT(env);
|
||
|
evalArg= eval(evalArg, evalEnv ? evalEnv : env);
|
||
|
GC_UNPROTECT(args);
|
||
|
return evalArg;
|
||
|
}
|
||
|
|
||
|
Cell *applySubr(Cell *args, Cell *env) { return apply(car(args), cdr(args), env); }
|
||
|
Cell *consSubr(Cell *args, Cell *env) { return cons(car(args), cadr(args)); }
|
||
|
Cell *rplacaSubr(Cell *args, Cell *env) { return rplaca(car(args), cadr(args)); }
|
||
|
Cell *rplacdSubr(Cell *args, Cell *env) { return rplacd(car(args), cadr(args)); }
|
||
|
Cell *carSubr(Cell *args, Cell *env) { return caar(args); }
|
||
|
Cell *cdrSubr(Cell *args, Cell *env) { return cdar(args); }
|
||
|
Cell *assqSubr(Cell *args, Cell *env) { return assq(car(args), cadr(args)); }
|
||
|
|
||
|
Cell *printlnSubr(Cell *args, Cell *env)
|
||
|
{
|
||
|
for (; args; args= cdr(args)) {
|
||
|
print(car(args), stdout);
|
||
|
if (cdr(args)) putchar(' ');
|
||
|
}
|
||
|
putchar('\n');
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#define arithmetic(name, id, op) \
|
||
|
Cell *name##Subr(Cell *args, Cell *env) \
|
||
|
{ \
|
||
|
if (cdr(args)) { \
|
||
|
long n= number(car(args)); \
|
||
|
for (args= cdr(args); args; args= cdr(args)) \
|
||
|
n op##= number(car(args)); \
|
||
|
return mkNumber(n); \
|
||
|
} \
|
||
|
return mkNumber(id op number(car(args))); \
|
||
|
}
|
||
|
|
||
|
arithmetic(add, 0, +)
|
||
|
arithmetic(subtract, 0, -)
|
||
|
arithmetic(multiply, 1, *)
|
||
|
arithmetic(divide, 1, /)
|
||
|
arithmetic(modulus, 1, %)
|
||
|
|
||
|
#undef arithmetic
|
||
|
|
||
|
#define relation(name, op) \
|
||
|
Cell *name##Subr(Cell *args, Cell *env) \
|
||
|
{ \
|
||
|
Cell *numbers; \
|
||
|
for (numbers= args; cdr(numbers); numbers= cdr(numbers)) \
|
||
|
if (!(number(car(numbers)) op number(cadr(numbers)))) \
|
||
|
return 0; \
|
||
|
return args; \
|
||
|
}
|
||
|
|
||
|
relation(less, < )
|
||
|
relation(lessEqual, <=)
|
||
|
relation(equal, ==)
|
||
|
relation(notEqual, !=)
|
||
|
relation(greaterEqual, >=)
|
||
|
relation(greater, > )
|
||
|
|
||
|
#undef relation
|
||
|
|
||
|
int numbersP2(Cell *args) { return numberP(car(args)) && numberP(cadr(args)); }
|
||
|
int numbersP3(Cell *args) { return numberP(car(args)) && numberP(cadr(args)) && numberP(caddr(args)); }
|
||
|
|
||
|
Cell *primToStringSubr(Cell *args) { return numberP(car(args)) ? mkString(strdup((char *)number(car(args)))) : 0; }
|
||
|
|
||
|
typedef void *ptr;
|
||
|
|
||
|
#define access(type) \
|
||
|
Cell *type##PeekSubr(Cell *args, Cell *env) { return numbersP2(args) ? mkNumber((long)((type *)number(car(args)))[number(cadr(args))]) : 0; } \
|
||
|
Cell *type##PokeSubr(Cell *args, Cell *env) { if (numbersP3(args)) ((type *)number(car(args)))[number(cadr(args))]= (type)number(caddr(args)); return caddr(args); }
|
||
|
|
||
|
access(char)
|
||
|
access(short)
|
||
|
access(int)
|
||
|
access(long)
|
||
|
access(ptr)
|
||
|
|
||
|
#undef access
|
||
|
|
||
|
#include <dlfcn.h>
|
||
|
|
||
|
void *rtldDefault= 0;
|
||
|
|
||
|
Cell *dlsymSubr(Cell *args, Cell *env) { return stringP(car(args)) ? mkPsubr(dlsym(rtldDefault, string(car(args)))) : 0; }
|
||
|
Cell *fsubrSubr(Cell *args, Cell *env) { return psubrP (car(args)) ? mkFsubr(psubr(car(args))) : 0; }
|
||
|
Cell *subrSubr (Cell *args, Cell *env) { return psubrP (car(args)) ? mkSubr (psubr(car(args))) : 0; }
|
||
|
|
||
|
int xFlag= 0;
|
||
|
int vFlag= 0;
|
||
|
|
||
|
Cell *repl(FILE *in)
|
||
|
{
|
||
|
Cell *expr= 0, *value= 0;
|
||
|
GC_PROTECT(expr);
|
||
|
GC_PROTECT(value);
|
||
|
while (!feof(in)) {
|
||
|
if (isatty(fileno(in))) {
|
||
|
printf("> ");
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
expr= readFile(in);
|
||
|
if (CEOF == expr) break;
|
||
|
if (xFlag) println(expr, stderr);
|
||
|
if (expr) {
|
||
|
value= eval(expr, globals);
|
||
|
if (isatty(fileno(in))) println(value, stderr);
|
||
|
if (vFlag) { fprintf(stderr, "==> "); println(value, stderr); }
|
||
|
}
|
||
|
}
|
||
|
GC_UNPROTECT(expr);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
#if !BDWGC
|
||
|
void markFunction(void *ptr)
|
||
|
{
|
||
|
Cell *cell= (Cell *)ptr;
|
||
|
assert(ptr);
|
||
|
switch (cell->mTag) {
|
||
|
case Number: case String: case Symbol: case Subr: case Fsubr:
|
||
|
return;
|
||
|
case Cons:
|
||
|
case Expr:
|
||
|
case Fexpr:
|
||
|
if (cell->mCons.a) GC_mark(cell->mCons.a);
|
||
|
if (cell->mCons.d) GC_mark(cell->mCons.d);
|
||
|
return;
|
||
|
default:
|
||
|
fatal("unknown tag");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void freeFunction(void *ptr)
|
||
|
{
|
||
|
Cell *cell= (Cell *)ptr;
|
||
|
switch (cell->mTag) {
|
||
|
case String: free((void *)string(cell)); return;
|
||
|
case Symbol: free((void *)symbol(cell)); return;
|
||
|
default: return;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
#if BDWGC
|
||
|
GC_INIT();
|
||
|
#else
|
||
|
GC_mark_function= markFunction;
|
||
|
GC_free_function= freeFunction;
|
||
|
#endif
|
||
|
|
||
|
for (i= 0; i < 256; ++i) readers[i]= readIllegal;
|
||
|
initReaders(readBlank, " \t\n\v\f\r");
|
||
|
initReaders(readDigit, "0123456789");
|
||
|
initReaders(readAlpha, "abcdefghijklmnopqrstuvwxyz");
|
||
|
initReaders(readAlpha, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||
|
initReaders(readAlpha, "!#$%&*/:<=>?@\\^_|~");
|
||
|
initReaders(readSign, "+-");
|
||
|
initReaders(readString, "\"");
|
||
|
initReaders(readQuote, "'");
|
||
|
initReaders(readQquote, "`");
|
||
|
initReaders(readUquote, ",");
|
||
|
initReaders(readList, "([{");
|
||
|
initReaders(readAlpha, ".");
|
||
|
initReaders(readSemi, ";");
|
||
|
|
||
|
rtldDefault= dlopen(0, RTLD_NOW | RTLD_GLOBAL);
|
||
|
|
||
|
_S_t = intern("t");
|
||
|
_S_quote = intern("quote");
|
||
|
_S_qquote = intern("quasiquote");
|
||
|
_S_uquote = intern("unquote");
|
||
|
_S_uquotes = intern("unquote-splicing");
|
||
|
|
||
|
globals= cons(cons(intern("t" ), _S_t ), globals);
|
||
|
globals= cons(cons(intern("dlsym" ), mkSubr (dlsymSubr )), globals);
|
||
|
globals= cons(cons(intern("fsubr" ), mkSubr (fsubrSubr )), globals);
|
||
|
globals= cons(cons(intern("subr" ), mkSubr (subrSubr )), globals);
|
||
|
globals= cons(cons(intern("define" ), mkFsubr(defineFsubr )), globals);
|
||
|
|
||
|
globals= cons((syntaxTable= cons(intern("*syntax-table*"), 0)), globals);
|
||
|
|
||
|
GC_PROTECT(globals);
|
||
|
GC_PROTECT(interns);
|
||
|
|
||
|
if (argc == 1) repl(stdin);
|
||
|
else {
|
||
|
for (++argv; argc > 1; --argc, ++argv) {
|
||
|
if (!strcmp(*argv, "-v")) vFlag= 1;
|
||
|
else if (!strcmp(*argv, "-x")) xFlag= 1;
|
||
|
else if (!strcmp(*argv, "-" )) repl(stdin);
|
||
|
else {
|
||
|
FILE *in= fopen(*argv, "r");
|
||
|
if (!in) perror(*argv);
|
||
|
else {
|
||
|
repl(in);
|
||
|
fclose(in);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|