ref: a88cd71d79e142d686b01ff33624a4cc8febb268
dir: /io.c/
#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();
}