#include #include #include #include #include "apl9.h" Datum *getquotequad(void); void setquotequad(Datum *); Datum *getquad(void); void setquad(Datum *); Datum *getquadrawio(void); void setquadrawio(Datum *); Datum *getio(void); void setio(Datum *); Datum *getpp(void); void setpp(Datum *); Datum *getdiv(void); void setdiv(Datum *); Datum *geta(void); Datum *getd(void); Datum *geten(void); Datum *getself(void); Datum *getsession(void); void setsession(Datum *); /* Array *runfile(Array *); in apl9.h */ Array *quadem(Array *); Array *quadsignal1(Array *); Array *quadsignal2(Array *, Array *); Array *quadinfo(Array *); Array *quadproto(Array *); Array *quaducs(Array *); Array *quaddl(Array *); Array *quadthreads1(Array *); Array *quadthreads2(Array *, Array *); Array *quadserial(Array *, Array *); Array *quadopen(Array *, Array *); Array *quadcreate(Array *, Array *); Array *quadclose(Array *); Array *quadread(Array *, Array *); Array *quadwrite(Array *, Array *); Array *quadpipe(Array *); Array *quadfd2path(Array *); static Rune *quadquotebuf = nil; static Array *session = nil; QuadnameDef quadnames[] = { {L"⍞", NameTag, getquotequad, setquotequad, nil, nil}, {L"⎕", NameTag, getquad, setquad, nil, nil}, {L"⎕RAWIO", NameTag, getquadrawio, setquadrawio, nil, nil}, {L"⎕IO", NameTag, getio, setio, nil, nil}, {L"⎕PP", NameTag, getpp, setpp, nil, nil}, {L"⎕DIV", NameTag, getdiv, setdiv, nil, nil}, {L"⎕A", NameTag, geta, nil, nil, nil}, {L"⎕D", NameTag, getd, nil, nil, nil}, {L"⎕EN", NameTag, geten, nil, nil, nil}, {L"⎕SELF", NameTag, getself, nil, nil, nil}, {L"⎕SESSION", NameTag, getsession, setsession, nil, nil}, {L"⎕RUN", FunctionTag, nil, nil, runfile, nil}, {L"⎕EM", FunctionTag, nil, nil, quadem, nil}, {L"⎕SIGNAL", FunctionTag, nil, nil, quadsignal1, quadsignal2}, {L"⎕INFO", FunctionTag, nil, nil, quadinfo, nil}, {L"⎕PROTO", FunctionTag, nil, nil, quadproto, nil}, {L"⎕UCS", FunctionTag, nil, nil, quaducs, nil}, {L"⎕DL", FunctionTag, nil, nil, quaddl, nil}, {L"⎕THREADS", FunctionTag, nil, nil, quadthreads1, quadthreads2}, {L"⎕SERIAL", FunctionTag, nil, nil, nil, quadserial}, {L"⎕OPEN", FunctionTag, nil, nil, nil, quadopen}, {L"⎕CREATE", FunctionTag, nil, nil, nil, quadcreate}, {L"⎕CLOSE", FunctionTag, nil, nil, quadclose, nil}, {L"⎕READ", FunctionTag, nil, nil, nil, quadread}, {L"⎕WRITE", FunctionTag, nil, nil, nil, quadwrite}, {L"⎕PIPE", FunctionTag, nil, nil, quadpipe, nil}, {L"⎕FD2PATH", FunctionTag, nil, nil, quadfd2path, nil}, {nil, 0, nil, nil, nil, nil} /* MUST BE LAST */ }; void initquadnames(void) { Symbol *s; QuadnameDef q; int i; for(i = 0; quadnames[i].name != nil; i++){ q = quadnames[i]; if(q.tag == NameTag){ s = getsym(q.name, 1); s->getfn = q.get; s->setfn = q.set; } } } Datum * quadnamedatum(QuadnameDef q) { Datum *d = allocdatum(q.tag, 0); switch(q.tag){ case NameTag: d->name = runestrdup(q.name); break; case FunctionTag: d->func.type = FunctypeQuad; d->func.quad = emalloc(sizeof(QuadnameDef)); *d->func.quad = q; d->func.left = nil; break; case MonadicOpTag: case DyadicOpTag: default: print("Can't use quad names with type=%d\n", q.tag); exits(nil); } return d; } /* ⍞ */ Datum * getquotequad(void) { int sizemax = 512; int size; Rune *input; if(quadquotebuf != nil){ input = quadquotebuf; size = runestrlen(input); sizemax = size+1; quadquotebuf = nil; }else{ input = emalloc(sizeof(Rune) * sizemax); size = 0; } do{ if(size == sizemax-1){ sizemax += 512; input = erealloc(input, sizeof(Rune) * sizemax); } input[size] = Bgetrune(stdin); size++; }while(input[size-1] != '\n'); input[size-1] = 0; Datum *result = allocdatum(ArrayTag, 0); result->array = mkrunearray(input); free(input); return result; } void setquotequad(Datum *d) { Rune *str = ppdatum(d); Array *strarray = mkrunearray(str); if(session) rundfn(L"0::⎕RAWIO←⍶ ⋄ ('⍞' ⍺) ⍈ ⍵", d, nil, strarray, session); else setquadrawio(d); free(str); freearray(strarray); } /* ⎕ */ Datum * getquad(void) { return getquadrawio(); } void setquad(Datum *d) { Rune *str = ppdatum(d); Array *strarray = mkrunearray(str); if(session) rundfn(L"0::⎕RAWIO←⍶ ⋄ ('⎕' ⍺) ⍈ ⍵", d, nil, strarray, session); else setquadrawio(d); free(str); } /* for output from main loop */ void outputmain(Datum *d) { Rune *str = ppdatum(d); Array *data = allocarray(AtypeArray, 1, 2); data->shape[0] = 2; data->arraydata[0] = mkscalarint(d->shy); data->arraydata[1] = mkrunearray(str); if(session) rundfn(L"0::⎕RAWIO←⍶ ⋄ ('→' (⍺)) ⍈ ⍵", d, nil, data, session); else setquadrawio(d); free(str); freearray(data); } /* ⎕RAWIO */ Datum * getquadrawio(void) { print("⎕:\n\t"); Datum *result = evalline(nil, nil, 1); /* TODO check that the expression doesn't fail */ return result; } void setquadrawio(Datum *new) { if(new->tag == ArrayTag && GetType(new->array) == AtypeRune){ for(int i = 0; i < new->array->size; i++) print("%C", new->array->runedata[i]); return; } Rune *str = ppdatum(new); print("%S", str); free(str); } /* ⎕IO */ Datum * getio(void) { Datum *d = allocdatum(ArrayTag, 0); d->array = mkscalarint(globalIO()); return d; } void setio(Datum *new) { if(new->tag != ArrayTag || GetRank(new->array) != 0 || GetType(new->array) != AtypeInt || (new->array->intdata[0] != 0 && new->array->intdata[0] != 1)) throwerror(nil, EDomain); else globalIOset(new->array->intdata[0]); } /* ⎕PP */ Datum * getpp(void) { Datum *d = allocdatum(ArrayTag, 0); d->array = mkscalarint(printprecision); return d; } void setpp(Datum *new) { if(new->tag != ArrayTag || GetRank(new->array) != 0 || GetType(new->array) != AtypeInt || new->array->intdata[0] < 0) throwerror(nil, EDomain); else printprecision = new->array->intdata[0]; } /* ⎕DIV */ Datum * getdiv(void) { Datum *d = allocdatum(ArrayTag, 0); d->array = mkscalarint(globalDIV()); return d; } void setdiv(Datum *new) { if(new->tag != ArrayTag || GetRank(new->array) != 0 || GetType(new->array) != AtypeInt || (new->array->intdata[0] != 0 && new->array->intdata[0] != 1)) throwerror(nil, EDomain); else globalDIVset(new->array->intdata[0]); } /* ⎕A */ Datum * geta(void) { Datum *d = allocdatum(ArrayTag, 0); d->array = mkrunearray(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"); return d; } /* ⎕D */ Datum * getd(void) { Datum *d = allocdatum(ArrayTag, 0); d->array = mkrunearray(L"0123456789"); return d; } /* ⎕EN */ Datum * geten(void) { Datum *d = allocdatum(ArrayTag, 0); ThreadData *td = getthreaddata(); d->array = mkscalarint(td->lasterror); return d; } /* ⎕SELF */ Datum * getself(void) { Datum *d = allocdatum(ArrayTag, 0); d->array = mkscalarint(getpid()); return d; } /* ⎕SELF */ Datum * getsession(void) { Datum *d = allocdatum(ArrayTag, 0); d->array = fnSame(session); return d; } void setsession(Datum *new) { if(new->tag != ArrayTag || GetRank(new->array) != 0 || GetType(new->array) != AtypeInt) throwerror(nil, EDomain); session = fnSame(new->array); } /* ⎕RUN */ Array * runfile(Array *a) { if(GetType(a) != AtypeRune || GetRank(a) > 1){ return mkscalarint(0); } char *filename = smprint("%S", pparray(a)); Biobuf *bio = Bopen(filename, OREAD); if(bio == nil) return mkscalarint(0); while(1){ Rune r = Bgetrune(bio); Bungetrune(bio); if(r == Beof) break; else evalline(nil, bio, 1); } Bterm(bio); return mkscalarint(1); } /* ⎕EM */ Array * quadem(Array *codes) { if(GetType(codes) != AtypeInt) throwerror(nil, EDomain); Array *res; if(codes->size == 1){ Rune *msg = errorstr(codes->intdata[0]); if(runestrlen(msg) == 0){ msg = runesmprint("ERROR NUMBER %lld", codes->intdata[0]); res = mkrunearray(msg); free(msg); }else res = mkrunearray(msg); }else{ res = duparrayshape(codes, AtypeArray); for(int i = 0; i < codes->size; i++){ Array *code = arrayitem(codes, i); res->arraydata[i] = quadem(code); freearray(code); } } return res; } /* ⎕SIGNAL */ Array * quadsignal1(Array *code) { if(GetType(code) != AtypeInt) throwerror(nil, EDomain); if(code->size != 1) throwerror(nil, ELength); throwerror(nil, code->intdata[0]); return nil; } Array * quadsignal2(Array *msg, Array *code) { if(GetType(code) != AtypeInt || GetType(msg) != AtypeRune) throwerror(nil, EDomain); if(code->size != 1) throwerror(nil, ELength); if(GetRank(msg) > 1) throwerror(nil, ERank); throwerror(pparray(msg), code->intdata[0]); return nil; } /* ⎕INFO */ Array * quadinfo(Array *a) { if(GetType(a) != AtypeRune) throwerror(nil, EDomain); Rune *code = pparray(a); Datum *res = evalline(code, nil, 0); Rune *info; switch(res->tag){ case ArrayTag:{ char *typestring = "?"; switch(GetType(res->array)){ case AtypeInt: typestring = "int"; break; case AtypeFloat: typestring = "float"; break; case AtypeRune: typestring = "rune"; break; case AtypeMixed: typestring = "mixed"; break; case AtypeArray: typestring = "array"; break; } uvlong size = arrayspaceused(res->array); info = runesmprint("Type = %s, size in bytes = %ulld", typestring, size); break; } default: info = runesmprint("Can't show info for datum tag %d", res->tag); } Array *infoarr = mkrunearray(info); free(code); free(res); free(info); return infoarr; } /* ⎕PROTO */ Array * quadproto(Array *a) { return fillelement(a); } /* ⎕UCS */ Array * quaducs(Array *a) { Array *res = nil; if(GetType(a) == AtypeInt){ res = duparrayshape(a, AtypeRune); for(int i = 0; i < res->size; i++) res->runedata[i] = a->intdata[i]; }else if(GetType(a) == AtypeRune){ res = duparrayshape(a, AtypeInt); for(int i = 0; i < res->size; i++) res->intdata[i] = a->runedata[i]; }else throwerror(nil, EDomain); return res; } /* ⎕DL */ Array * quaddl(Array *a) { /* TODO: return amount of seconds slept */ if(a->size != 1) throwerror(nil, ELength); if(GetType(a) != AtypeInt && GetType(a) != AtypeFloat) throwerror(nil, EDomain); if(GetType(a) == AtypeInt && a->intdata[0] >= 0) sleep(a->intdata[0] * 1000); else if(GetType(a) == AtypeFloat && a->floatdata[0] >= 0) sleep(a->floatdata[0] * 1000); else throwerror(nil, EDomain); return fnSame(a); } /* ⎕THREADS */ Array * quadthreads1(Array *properties) { Array *threadids = runningthreads(); Array *res = quadthreads2(threadids, properties); freearray(threadids); return res; } Array * quadthreads2(Array *thread, Array *property) { if(GetType(thread) != AtypeInt || GetType(property) != AtypeInt) throwerror(nil, EDomain); if(GetRank(thread) > 1 || GetRank(property) > 1) throwerror(nil, ERank); if(GetRank(thread) > 0 || GetRank(property) > 0) return rundfn(L"⍺ ⎕THREADS⌾ ⍵", nil, nil, thread, property); /* Get thread with ID thread, and property based on number in property */ return threadproperty(thread->intdata[0], property->intdata[0]); } /* ⎕SERIAL */ Array * quadserial(Array *mode, Array *a) { if(GetType(mode) != AtypeInt || mode->size != 1) throwerror(nil, EDomain); int m = mode->intdata[0]; if(m != 0 && m != 1) throwerror(L"Left argument of ⎕serial must be 0 or 1", EDomain); /* TODO: byte ordering */ Array *result; if(m == 0){ /* serialize */ Array *header = allocarray(AtypeInt, 1, 2+GetRank(a)); header->shape[0] = header->size; header->intdata[0] = GetType(a); header->intdata[1] = GetRank(a); for(int i = 0; i < GetRank(a); i++) header->intdata[2+i] = a->shape[i]; Array *body; if(GetType(a) == AtypeArray) /* nested */ body = rundfn(L"⊃,⌿0⎕SERIAL¨,⍵", nil, nil, nil, a); else{ int len = datasizes[GetType(a)] * a->size; body = allocarray(AtypeInt, 1, len); body->shape[0] = len; for(int i = 0; i < len; i++) body->intdata[i] = a->rawdata[i]; } result = fnCatenateFirst(header, body); freearray(header); freearray(body); }else{ /* un-serialize */ if(GetRank(a) != 1 || GetType(a) != AtypeInt) throwerror(nil, EDomain); int type = a->intdata[0]; int rank = a->intdata[1]; vlong size = 1; for(int i = 0; i < rank; i++) size *= a->intdata[i+2]; if(type == AtypeArray){ /* nested */ int skips[512]; int depth = 0; Array *starts = allocarray(AtypeInt, 1, a->size); starts->shape[0] = starts->size; for(int i = 0; i < starts->size; i++) starts->intdata[i] = 0; int offset = 2+rank; skips[0] = 0; while(offset < a->size){ if(depth == 0 && skips[0] == 0) starts->intdata[offset] = 1; int type = a->intdata[offset]; int rank = a->intdata[offset+1]; vlong size = 1; for(int i = 0; i < rank; i++) size *= a->intdata[i+offset+2]; offset += 2+rank; if(type == AtypeArray){ depth++; skips[depth] = size; }else{ if(skips[depth] > 0) skips[depth]--; if(skips[depth] == 0 && depth > 0){ depth--; if(skips[depth] > 0) skips[depth]--; } offset += size*datasizes[type]; } } Array *parts = rundfn(L"1⎕SERIAL¨(+⍀⍺)⊆⍵", nil, nil, starts, a); Array *shape = allocarray(AtypeInt, 1, rank); shape->shape[0] = rank; for(int i = 0; i < rank; i++) shape->intdata[i] = a->intdata[2+i]; result = fnReshape(shape, parts); freearray(parts); freearray(starts); }else{ result = allocarray(type, rank, size); for(int i = 0; i < rank; i++) result->shape[i] = a->intdata[i+2]; int len = datasizes[type] * size; for(int i = 0; i < len; i++) result->rawdata[i] = a->intdata[i+2+rank]; } } return result; } /* ⎕OPEN */ Array * quadopen(Array *file, Array *omode) { if(GetType(file) != AtypeRune || GetRank(file) > 1) throwerror(L"Invalid file name", EDomain); if(GetType(omode) != AtypeInt || omode->size != 1) throwerror(L"Invalid mode", EDomain); Rune *tmp = pparray(file); char *filename = smprint("%S", tmp); int mode = omode->intdata[0]; int ret = open(filename, mode); free(tmp); free(filename); return mkscalarint(ret); } /* ⎕CREATE */ Array * quadcreate(Array *file, Array *arg) { if(GetType(file) != AtypeRune || GetRank(file) > 1) throwerror(L"Invalid file name", EDomain); if(GetType(arg) != AtypeInt || GetRank(arg) != 1 || arg->size != 2) throwerror(L"Invalid mode+perm", EDomain); Rune *tmp = pparray(file); char *filename = smprint("%S", tmp); int ret = create(filename, arg->intdata[0], arg->intdata[1]); free(tmp); free(filename); return mkscalarint(ret); } /* ⎕CLOSE */ Array * quadclose(Array *fd) { if(fd->size != 1 || GetType(fd) != AtypeInt) throwerror(nil, EDomain); int ret = close(fd->intdata[0]); return mkscalarint(ret); } /* ⎕READ */ Array * quadread(Array *fd, Array *nbytes) { if(GetType(fd) != AtypeInt || fd->size != 1) throwerror(L"Invalid fd", EDomain); if(GetType(nbytes) != AtypeInt || nbytes->size != 1 || nbytes->intdata[0] < 1) throwerror(L"Invalid byte coint", EDomain); u8int *buf = emalloc(nbytes->intdata[0]); long nread = read(fd->intdata[0], buf, nbytes->intdata[0]); if(nread >= 0) buf = erealloc(buf, nread); else{ free(buf); throwerror(L"Read failed", EDomain); } Array *res = allocarray(AtypeInt, 1, nread); res->shape[0] = nread; for(int i = 0; i < nread; i++) res->intdata[i] = buf[i]; free(buf); return res; } /* ⎕WRITE */ Array * quadwrite(Array *fd, Array *data) { if(GetType(fd) != AtypeInt || fd->size != 1) throwerror(L"Invalid fd", EDomain); if((GetType(data) != AtypeInt && GetType(data) != AtypeFloat) || GetRank(data) > 1) throwerror(L"Data must be a scalar or vector of bytes!", EDomain); u8int *raw = emalloc(data->size); for(int i = 0; i < data->size; i++){ u8int v; switch(GetType(data)){ case AtypeInt: v = data->intdata[i]; break; case AtypeFloat: v = data->floatdata[i]; break; default: v = 0; break; } raw[i] = v; } long ret = write(fd->intdata[0], raw, data->size); free(raw); return mkscalarint(ret); } /* ⎕PIPE */ Array * quadpipe(Array *name) { Array *result; int p[2]; if(name->size > 0 && (GetType(name) != AtypeRune || GetRank(name) > 1)) throwerror(L"Invalid pipe name", EDomain); if(name->size == 0){ /* Unnamed pipe */ pipe(p); result = allocarray(AtypeInt, 1, 2); result->shape[0] = 2; result->intdata[0] = p[0]; result->intdata[1] = p[1]; }else{ /* If the name exists, open that pipe, otherwise create a new one */ Rune *tmp = pparray(name); char *name = smprint("/srv/%S", tmp); free(tmp); int fd = open(name, ORDWR); if(fd > 0) result = mkscalarint(fd); else{ fd = create(name, OWRITE, 0666); if(fd < 0) throwerror(L"Named pipe could not be opened", EDomain); pipe(p); fprint(fd, "%d", p[0]); close(fd); close(p[0]); result = mkscalarint(p[1]); } free(name); } return result; } /* ⎕FD2PATH */ Array * quadfd2path(Array *fd) { if(GetType(fd) != AtypeInt || fd->size != 1) throwerror(L"Invalid fd", EDomain); char *buf = emalloc(1024); int ret = fd2path(fd->intdata[0], buf, 1024); if(ret != 0) throwerror(nil, EDomain); Rune *path = runesmprint("%s", buf); Array *res = mkrunearray(path); free(buf); free(path); return res; }