#include #include #include #include #include #include "paper.h" #include "impl.h" char symbol[Strsize]; Lsym* hash[Hashsize]; jmp_buf err; void cvtatof(Node*, Node**, int); void cvtatoi(Node*, Node**, int); void cvtitoa(Node*, Node**, int); void bprint(Node*, Node**, int); void funcbound(Node*, Node**, int); void getfile(Node*, Node**, int); void match(Node*, Node**, int); void doerror(Node*, Node**, int); void doaccess(Node*, Node**, int); void readfile(Node*, Node**, int); void interpret(Node*, Node**, int); void include(Node*, Node**, int); void regexp(Node*, Node**, int); void dogetfields(Node*, Node**, int); void dotype(Node*,Node**, int); void doassert(Node*,Node**, int); void dolen(Node*,Node**, int); typedef struct Btab Btab; struct Btab { char *name; void (*fn)(Node*, Node**, int); } tab[] = { "atof", cvtatof, "atoi", cvtatoi, "error", doerror, "file", getfile, "readfile", readfile, "access", doaccess, "getfields", dogetfields, "itoa", cvtitoa, "match", match, "print", bprint, "interpret", interpret, "include", include, "regexp", regexp, "type", dotype, "assert", doassert, "len", dolen, 0 }; void installbuiltin(void) { Btab *b; Lsym *l; b = tab; while(b->name){ ppbind(b->name, b->fn); b++; } /* nil value */ l = mkvar("nil"); l->v->store.type = TLIST; l->v->set = 1; } void match(Node *r, Node **av, int na) { int i; List *f; Node resi, resl; if(na != 2) pperror("match(obj, list): arg count"); ppexpr(av[1], &resl); if(resl.store.type != TLIST) pperror("match(obj, list): need list"); ppexpr(av[0], &resi); r->op = OCONST; r->store.type = TINT; r->store.ival = -1; i = 0; for(f = resl.store.l; f; f = f->next) { if(resi.store.type == f->store.type) { switch(resi.store.type) { case TINT: if(resi.store.ival == f->store.ival) { r->store.ival = i; return; } break; case TFLOAT: if(resi.store.fval == f->store.fval) { r->store.ival = i; return; } break; case TSTRING: if(scmp(resi.store.string, f->store.string)) { r->store.ival = i; return; } break; case TLIST: pperror("match(obj, list): not defined for list"); } } i++; } } void interpret(Node *r, Node **av, int na) { int rv; Node res; if(na == 0) pperror("interpret(string): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("interpret(string): arg type"); pushstr(&res); rv = yyparse(); popio(); if(rv != 0) pperror("interpret: %r"); r->store.ival = rv; r->op = OCONST; r->store.type = TINT; } void include(Node *r, Node **av, int na) { int rv; Node res; if(na == 0) pperror("include(string): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("include(string): arg type"); pushfile(res.store.string->string); rv = yyparse(); popio(); if(rv != 0) pperror("include: %r"); r->store.ival = rv; r->op = OCONST; r->store.type = TINT; } void doerror(Node *r, Node **av, int na) { Node res; USED(r); if(na == 0) pperror("error(string): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("error(string): arg type"); pperror(res.store.string->string); } void doaccess(Node *r, Node **av, int na) { Node res; if(na == 0) pperror("access(filename): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("access(filename): arg type"); r->op = OCONST; r->store.type = TINT; r->store.ival = 0; if(access(res.store.string->string, 4) == 0) r->store.ival = 1; } void readfile(Node *r, Node **av, int na) { Node res; int n, fd; char *buf; Dir *db; if(na == 0) pperror("readfile(filename): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("readfile(filename): arg type"); fd = open(res.store.string->string, OREAD); if(fd < 0) return; db = dirfstat(fd); if(db == nil || db->length == 0) n = 8192; else n = db->length; free(db); buf = malloc(n); n = read(fd, buf, n); if(n > 0) { r->op = OCONST; r->store.type = TSTRING; r->store.string = strnodlen(buf, n); } free(buf); close(fd); } void getfile(Node *r, Node **av, int na) { int n; char *p; Node res; String *s; Biobuf *bp; List **l, *new; if(na == 0) pperror("file(filename): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("file(filename): arg type"); r->op = OCONST; r->store.type = TLIST; r->store.l = 0; p = res.store.string->string; bp = Bopen(p, OREAD); if(bp == 0) return; l = &r->store.l; for(;;) { p = Brdline(bp, '\n'); n = Blinelen(bp); if(p == 0) { if(n == 0) break; s = strnodlen(0, n); Bread(bp, s->string, n); } else s = strnodlen(p, n-1); new = pplist(TSTRING); new->store.string = s; *l = new; l = &new->next; } Bterm(bp); } void cvtatof(Node *r, Node **av, int na) { Node res; if(na == 0) pperror("atof(string): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("atof(string): arg type"); r->op = OCONST; r->store.type = TFLOAT; r->store.fval = atof(res.store.string->string); } void cvtatoi(Node *r, Node **av, int na) { Node res; if(na == 0) pperror("atoi(string): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("atoi(string): arg type"); r->op = OCONST; r->store.type = TINT; r->store.ival = strtoull(res.store.string->string, 0, 0); } static char *fmtflags = "-0123456789. #,u"; static char *fmtverbs = "bdox"; static int acidfmt(char *fmt, char *buf, int blen) { char *r, *w, *e; w = buf; e = buf+blen; for(r=fmt; *r; r++){ if(w >= e) return -1; if(*r != '%'){ *w++ = *r; continue; } if(*r == '%'){ *w++ = *r++; if(*r == '%'){ if(w >= e) return -1; *w++ = *r; continue; } while(*r && strchr(fmtflags, *r)){ if(w >= e) return -1; *w++ = *r++; } if(*r == 0 || strchr(fmtverbs, *r) == nil) return -1; if(w+3 > e) return -1; *w++ = 'l'; *w++ = 'l'; *w++ = *r; } } if(w >= e) return -1; *w = 0; return 0; } void cvtitoa(Node *r, Node **av, int na) { Node res; vlong ival; char buf[128], fmt[32]; if(na == 0 || na > 2) pperror("itoa(number [, fmt]): arg count"); ppexpr(av[0], &res); if(res.store.type != TINT) pperror("itoa(number [, fmt]): arg type"); ival = res.store.ival; strncpy(fmt, "%lld", sizeof(fmt)); if(na == 2){ ppexpr(av[1], &res); if(res.store.type != TSTRING) pperror("itoa(number [, fmt]): fmt type"); if(acidfmt(res.store.string->string, fmt, sizeof(fmt))) pperror("itoa(number [, fmt]): malformed fmt"); } snprint(buf, sizeof(buf), fmt, ival); r->op = OCONST; r->store.type = TSTRING; r->store.string = strnode(buf); } void flatten(Node **av, Node *n, int *na) { if(n == 0) return; switch(n->op) { case OLIST: flatten(av, n->left, na); flatten(av, n->right, na); break; default: av[*na] = n; (*na)++; if(*na >= Maxarg) pperror("too many function arguments"); break; } } void regerror(char *msg) { pperror(msg); } void regexp(Node *r, Node **av, int na) { Node res; Reprog *rp; if(na != 2) pperror("regexp(pattern, string): arg count"); ppexpr(av[0], &res); if(res.store.type != TSTRING) pperror("regexp(pattern, string): pattern must be string"); rp = regcomp(res.store.string->string); if(rp == 0) return; ppexpr(av[1], &res); if(res.store.type != TSTRING) pperror("regexp(pattern, string): bad string"); r->store.type = TINT; r->store.ival = regexec(rp, res.store.string->string, 0, 0); free(rp); } void patom(Store *res) { char *tnam; switch(res->type){ case TINT: print("%lld", res->ival); break; case TFLOAT: print("%g", res->fval); break; case TSTRING: print("%s", res->string->string); break; case TPOINTER: print("%#p", (void*)res->ival); break; default: tnam = ""; if(res->type < nelem(pptypenames)) tnam = pptypenames[res->type]; sysfatal("unhandled type %s (%hhud) in patom", tnam, res->type); } } void blprint(List *l) { print("{"); while(l) { switch(l->store.type) { default: patom(&l->store); break; case TSTRING: print("\""); patom(&l->store); print("\""); break; case TLIST: blprint(l->store.l); break; case TCODE: pcode(l->store.cc, 0); break; } l = l->next; if(l) print(", "); } print("}"); } void bprint(Node *r, Node **av, int na) { int i, nas; Node res; USED(r); nas = na; for(i = 0; i < nas; i++) { ppexpr(av[i], &res); switch(res.store.type) { default: patom(&res.store); break; case TCODE: pcode(res.store.cc, 0); break; case TLIST: blprint(res.store.l); break; } } } void dogetfields(Node *r, Node **av, int na) { Node nstr, ndelim, nmultif; char *buf; char *f[128]; int rc, i; List *l, **lp; if(na != 3) pperror("getfields(str, delims, multiflag): arg count"); ppexpr(av[0], &nstr); ppexpr(av[1], &ndelim); ppexpr(av[2], &nmultif); if(nstr.store.type != TSTRING || ndelim.store.type != TSTRING) pperror("getfields(str, delims, multiflag): arg type"); buf = strdup(nstr.store.string->string); if(buf == nil) sysfatal("out of memory"); rc = getfields(buf, f, nelem(f), bool(&nmultif), ndelim.store.string->string); lp = &r->store.l; for(i = 0; i < rc; i++){ l = pplist(TSTRING); l->store.string = strnode(f[i]); *lp = l; lp = &l->next; } r->op = OCONST; r->store.type = TLIST; free(buf); } void dotype(Node *r, Node **av, int na) { Node res; if(na == 0) pperror("type: expected argument"); if(av[0]->op == OLIST) pperror("type: arg count"); ppexpr(av[0], &res); r->op = OCONST; r->store.type = TSTRING; r->store.string = strnode(pptypenames[res.store.type]); } void doassert(Node *r, Node **av, int na) { Node res, str; if(na != 1 && na != 2) pperror("assert(expr [, str]): arg count"); if(na == 2){ ppexpr(av[1], &str); if(str.store.type != TSTRING) pperror("assert(expr [, str]): invalid string argument"); } ppexpr(av[0], &res); if(bool(&res) != 1){ if(na == 2) pperror(str.store.string->string); else pperror("assert failed"); } r->op = OCONST; r->store.type = res.store.type; r->store = res.store; } void dolen(Node *r, Node **av, int na) { vlong nlist; Node res; List *l; r->op = OCONST; r->store.type = TINT; if(na == 0) pperror("len: expected argument"); ppexpr(av[0], &res); switch(res.store.type){ default: pperror("len: invalid argument type; expected list or string"); break; case TSTRING: r->store.ival = res.store.string->len; break; case TLIST: nlist = 0; for(l = res.store.l; l != nil; l = l->next) nlist++; r->store.ival = nlist; break; } }