shithub: mlisp

ref: a88cd71d79e142d686b01ff33624a4cc8febb268
dir: /io.c/

View raw version
#include "lisp.h"

Stream sysout, sysin;

void
initio(void)
{
	sysout.type = IO_FILE;
	sysout.file = stdout;
	sysin.type = IO_FILE;
	sysin.file = stdin;
}

void
initbuf(Strbuf *buf)
{
	buf->buf = nil;
	buf->pos = 0;
	buf->len = 0;
}
void
freebuf(Strbuf *buf)
{
	free(buf->buf);
}
void
pushchar(Strbuf *buf, char c)
{
	if(buf->buf == nil){
		buf->len = 128;
		buf->buf = malloc(buf->len);
	}
	while(buf->pos >= buf->len){
		buf->len *= 2;
		buf->buf = realloc(buf->buf, buf->len);
	}
	buf->buf[buf->pos++] = c;
}


/*
 * output
 */

void
prf(char *fmt, ...)
{
	char *s, *p;
	va_list ap;
	va_start(ap, fmt);
	s = vsmprint(fmt, ap);
	va_end(ap);
	switch(sysout.type){
	case IO_FILE:
		fwrite(s, 1, strlen(s), sysout.file);
		break;
	case IO_BUF:
		for(p = s; *p != '\0'; p++)
			pushchar(&sysout.strbuf, *p);
		break;
	}
	free(s);
}
void
tyo(char c)
{
	switch(sysout.type){
	case IO_FILE:
		putc(c, sysout.file);
		break;
	case IO_BUF:
		pushchar(&sysout.strbuf, c);
		break;
	}
}

/* figure out whether |...| are needed to print symbol.
 * TODO: actually fix this */
static int
escname(char *s)
{
	if(*s == '\0') return 1;
	for(; *s != '\0'; s++)
		if(islower(*s) || strchr(" \t\n\r()'#\"", *s))
			return 1;
	return 0;
}

void
printatom(C *c, int x)
{
	if(c == nil)
		prf("NIL");
	else if(fixnump(c))
		prf("%lld", (long long int)c->fix);
	else if(flonump(c))
		prf("%f", c->flo);
	else if(stringp(c)){
		if(x)
			prf("%s", c->str);
		else
			prf("\"%s\"", c->str);
	}else{
		assert(atom(c));
		for(; c != nil; c = c->d)
			if(c->a == pname){
				c = c->d->a;
				assert(stringp(c));
				if(!x && escname(c->str))
					prf("|%s|", c->str);
				else
					prf("%s", c->str);
				return;
			}
		prf("%%ATOM%%");
	}
}

void
printsxp(C *c, int x)
{
	int fst;
	if(c != nil && !cellp(c))
		prf("#%p", ((F*)c)->p);
	else if(atom(c))
		printatom(c, x);
	else{
		tyo('(');
		fst = 1;
		for(; c != nil; c = c->d){
			if(!cellp(c) || atom(c)){
				prf(" . ");
				printsxp(c, x);
				break;
			}
			if(!fst)
				tyo(' ');
			printsxp(c->a, x);
			fst = 0;
		}
		tyo(')');
	}
}

void
lprint(C *c)
{
	printsxp(c, 0);
}

void
princ(C *c)
{
	printsxp(c, 1);
}

/*
 * input
 */

int
tyi(void)
{
	switch(sysin.type){
	case IO_FILE:
		return getc(sysin.file);
	case IO_BUF:
		if(sysin.strbuf.pos >= sysin.strbuf.len)
			return EOF;
		return sysin.strbuf.buf[sysin.strbuf.pos++];
	}
	return EOF;
}

static int
chsp(void)
{
	int c;
	if(sysin.nextc){
		c = sysin.nextc;
		sysin.nextc = 0;
		return c;
	}
	c = tyi();
	// remove comments
	if(c == ';')
		while(c != '\n')
			c = tyi();
	if(isspace(c))
		c = ' ';
	return c;
}

static int
ch(void)
{
	int c;
	while(c = chsp(), c == ' ');
	return c;
}

C*
readnum(char *buf)
{
	int c;
	int type;
	fixnum oct;
	fixnum dec;
	flonum flo, fract, div;
	int sign;
	int ndigits;

	sign = 1;
	type = 0;	/* octal */
	oct = 0;
	dec = 0;
	flo = 0.0;
	fract = 0.0;
	div = 10.0;
	ndigits = 0;


	c = *buf;
	if(c == '-' || c == '+'){
		sign = c == '-' ? -1 : 1;
		buf++;
	}

	while(c = *buf++, c != '\0'){
		if(c >= '0' && c <= '9'){
			if(type == 0){
				oct = oct*8 + c-'0';
				dec = dec*10 + c-'0';
				flo = flo*10.0 + c-'0';
			}else{
				type = 2;	/* float */
				fract += (c-'0')/div;
				div *= 10.0;
			}
			ndigits++;
		}else if(c == '.' && type == 0){
			type = 1;	/* decimal */
		}else
			return nil;
	}
	if(ndigits == 0)
		return nil;
// use decimal default for now
//	if(type == 0)
//		return mkfix(sign*oct);
//	if(type == 1)
//		return mkfix(sign*dec);
	if(type == 0 || type == 1)
		return mkfix(sign*dec);
	return mkflo(sign*(flo+fract));
}

C*
readstr(void)
{
	C *s;
	int c;
	Strbuf buf;

	initbuf(&buf);
	while(c = chsp(), c != EOF){
		// TODO: some escapes
		if(c == '"')
			break;
		pushchar(&buf, c);
	}
	pushchar(&buf, '\0');
	s = mkstr(buf.buf);
	freebuf(&buf);
	return s;
}

C*
readatom(void)
{
	C *atm;
	int c;
	Strbuf buf;
	char *p;
	int spec, lc;

	spec = 0;
	lc = 1;
	initbuf(&buf);
	while(c = chsp(), c != EOF){
		if(!spec && strchr(" ()", c)){
			sysin.nextc = c;
			break;
		}
		if(c == '|'){
			lc = 0;
			spec = !spec;
			continue;
		}
		pushchar(&buf, c);
	}
	pushchar(&buf, '\0');
	if(lc)
		for(p = buf.buf; *p; p++)
			*p = toupper(*p);
	if(strcmp(buf.buf, "NIL") == 0){
		freebuf(&buf);
		return nil;
	}
	atm = readnum(buf.buf);
	if(atm == nil)
		atm = intern(buf.buf);
	freebuf(&buf);
	return atm;
}

C*
readlist(void)
{
	int first;
	int c;
	C **p;

	first = 1;
	p = push(nil);
	while(c = ch(), c != ')'){
		/* TODO: only valid when next letter is space */
		if(c == '.'){
			if(first)
				err("error: unexpected '.'");
			*p = readsxp(0);
			if(c = ch(), c != ')')
				err("error: expected ')' (got %c)", c);
			break;
		}
		sysin.nextc = c;
		*p = cons(readsxp(0), nil);
		p = &(*p)->d;
		first = 0;
	}
	return pop();
}

C*
readsxp(int eofok)
{
	int c;
	c = ch();
	if(c == EOF){
		if(eofok)
			return noval;
		err("error: EOF while reading s-exp");
	}
	if(c == '\'')
		return cons(quote, cons(readsxp(0), nil));
	if(c == '#'){
		c = ch();
		if(c == '\'')
			return cons(function, cons(readsxp(0), nil));
		err("expected '");
	}
	if(c == ')')
		err("error: unexpected ')'");
	if(c == '(')
		return readlist();
	if(c == '"')
		return readstr();
	sysin.nextc = c;
	return readatom();
}