From 920783505c3b91624200f144b2cbdd78dc348ca5 Mon Sep 17 00:00:00 2001 From: aiju Date: Sun, 27 Sep 2015 13:50:18 +0200 Subject: [PATCH] add timepic(1) --- sys/src/cmd/timepic.c | 729 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 729 insertions(+) create mode 100644 sys/src/cmd/timepic.c diff --git a/sys/src/cmd/timepic.c b/sys/src/cmd/timepic.c new file mode 100644 index 000000000..18deb55aa --- /dev/null +++ b/sys/src/cmd/timepic.c @@ -0,0 +1,729 @@ +#include +#include +#include +#include + +typedef struct Symbol Symbol; +typedef struct Event Event; +typedef struct Signal Signal; + +struct Symbol { + char *name; + int t; + double e; + Symbol *next; +}; +struct Event { + double t; + int val; + Event *next; + char *data; + int line; +}; +struct Signal { + char *name; + Event *ev; + Signal *next; +}; + +int lineno, lastc, peeked; +char sname[512]; +double sval; +double width, rheight; +double mint, maxt; +double left, right; +int nsig; +Biobuf *bp; +Symbol *stab[256]; +Signal *sigfirst, **siglast = &sigfirst; + +enum { + SYMFREE, + SYMNUM, +}; + +enum { + VZ, + VX, + VL, + VH, + VMULT, +}; + +enum { + CMD = -1, + SYM = -2, + NUM = -3, + EOF = -4, +}; + +static int +Tfmt(Fmt *f) +{ + int n; + + n = va_arg(f->args, int); + if(n >= 0 && n < 0x80 && isprint(n)) + return fmtprint(f, "'%c'", n); + else + switch(n){ + case CMD: return fmtprint(f, ".%s", sname); + case SYM: return fmtprint(f, "'%s'", sname); + case NUM: return fmtprint(f, "%g", sval); + case EOF: return fmtprint(f, "EOF"); + default: return fmtprint(f, "%d", n); + } +} + +static void * +emalloc(int n) +{ + void *v; + + v = malloc(n); + if(v == nil) + sysfatal("malloc: %r"); + memset(v, 0, n); + setmalloctag(v, getcallerpc(&n)); + return v; +} + +static void +error(int l, char *fmt, ...) +{ + va_list va; + char *s; + + va_start(va, fmt); + s = vsmprint(fmt, va); + fprint(2, "%d %s\n", l, s); + free(s); + va_end(va); +} + +static int +hash(char *s) +{ + int c; + + for(c = 0; *s != 0; s++) + c += *s; + return c; +} + +static Symbol * +getsym(char *st) +{ + Symbol *s, **p; + + for(p = &stab[hash(st)%nelem(stab)]; s = *p, s != nil; p = &s->next){ + if(strcmp(s->name, st) == 0) + return s; + } + s = emalloc(sizeof(Symbol)); + s->name = strdup(st); + *p = s; + return s; +} + +static int +issym(int c) +{ + return isalnum(c) || c == '_' || c >= 0x80; +} + +static int +numfsm(int c, int *st) +{ + enum { + PREDOT, + POSTDOT, + ESIGN, + EDIG + }; + + switch(*st){ + case PREDOT: + if(c == '.') + *st = POSTDOT; + if(c == 'e') + *st = ESIGN; + return isdigit(c) || c == '.' || c == 'e'; + case POSTDOT: + if(c == 'e') + *st = ESIGN; + return isdigit(c) || c == 'e'; + case ESIGN: + *st = EDIG; + return isdigit(c) || c == '+' || c == '-'; + case EDIG: + return isdigit(c); + } + return 0; +} + +static int +lex(void) +{ + int c; + char *p; + int st; + + do{ + c = Bgetc(bp); + if(c < 0) + return EOF; + if(!isspace(c)) + break; + if(c == '\n') + lineno++; + lastc = c; + }while(1); + + if(lastc == 10 && c == '.'){ + for(p = sname; c = Bgetc(bp), issym(c); ) + if(p < sname + sizeof(sname) - 1) + *p++ = c; + Bungetc(bp); + *p = 0; + return CMD; + } + if(isdigit(c) || c == '.'){ + st = 0; + for(p = sname; numfsm(c, &st); c = Bgetc(bp)) + if(p < sname + sizeof(sname) - 1) + *p++ = c; + Bungetc(bp); + *p = 0; + sval = strtol(sname, &p, 0); + if(*p != 0) + sval = strtod(sname, &p); + if(*p != 0) + error(lineno, "invalid number %s", sname); + return NUM; + } + if(issym(c)){ + for(p = sname; issym(c); c = Bgetc(bp)) + if(p < sname + sizeof(sname) - 1) + *p++ = c; + Bungetc(bp); + *p = 0; + return SYM; + } + return c; +} + +static int +next(void) +{ + int rc; + + if(peeked != 0){ + rc = peeked; + peeked = 0; + return rc; + } + return lex(); +} + +static int +peek(void) +{ + if(peeked == 0) + peeked = lex(); + return peeked; +} + +static void +expect(int n) +{ + int s; + + if((s = peek()) != n) + error(lineno, "expected %T, got %T", n, s); + else + next(); +} + +static double expr(void); + +static double +factor(void) +{ + int t; + double g; + Symbol *s; + + switch(t = next()){ + case NUM: + return sval; + case SYM: + s = getsym(sname); + if(s->t != SYMNUM) + error(lineno, "not a number: %s", s->name); + return s->e; + case '(': + g = expr(); + expect(')'); + return g; + default: + error(lineno, "factor: unexpected %T", t); + return 0; + } +} + +static double +term(void) +{ + double g; + + g = factor(); + while(peek() == '*' || peek() == '/'){ + switch(next()){ + case '*': g *= factor(); break; + case '/': g /= factor(); break; + } + } + return g; +} + +static double +expr(void) +{ + int s; + double g; + + s = 1; + if(peek() == '+' || peek() == '-') + s = next() == '-' ? -1 : 1; + g = s * term(); + while(peek() == '+' || peek() == '-'){ + s = next() == '-' ? -1 : 1; + g += s * term(); + } + return g; +} + +static void +assign(Symbol *s) +{ + next(); + if(s->t != SYMFREE){ + error(lineno, "symbol already exists: %s", s->name); + return; + } + s->t = SYMNUM; + s->e = expr(); + expect(';'); +} + +static int +parseval(Event *e) +{ + int t; + + switch(t = next()){ + case SYM: + if(strcmp(sname, "x") == 0) + return VX; + if(strcmp(sname, "z") == 0) + return VZ; + e->data = strdup(sname); + return VMULT; + case NUM: + if(sval == 0) + return VL; + if(sval == 1) + return VH; + e->data = smprint("%g", sval); + return VMULT; + default: + error(lineno, "unexpected %T", t); + return VZ; + } +} + +static void +append(double off, Event ***ep, Event *e) +{ + Event *f; + + for(; e != nil; e = e->next){ + f = emalloc(sizeof(Event)); + f->t = e->t + off; + f->val = e->val; + f->line = e->line; + if(e->data != nil) + f->data = strdup(e->data); + **ep = f; + *ep = &f->next; + } +} + +static void +freeev(Event *e) +{ + Event *en; + + for(; e != nil; e = en){ + en = e->next; + free(e->data); + free(e); + } +} + +static Event * +events(double *off, int ronly) +{ + int rela; + Event *e, *ev, **ep; + int i, n; + double f; + Symbol *sy; + double len; + int line; + + rela = 0; + line = 0; + ev = nil; + ep = &ev; + for(;;) + switch(peek()){ + case '+': + rela = 1; + next(); + break; + case '}': + case ';': + return ev; + case ':': + next(); + e = emalloc(sizeof(Event)); + e->t = *off; + e->val = parseval(e); + e->line = line; + line = 0; + *ep = e; + ep = &e->next; + break; + case '|': + line = 1; + next(); + break; + default: + f = expr(); + if(peek() == '{'){ + next(); + if(f < 0 || isNaN(f) || f >= 0x7fffffff){ + error(lineno, "invalid repeat count"); + f = 0; + } + len = 0; + e = events(&len, 1); + expect('}'); + n = f + 0.5; + for(i = 0; i < n; i++){ + append(*off, &ep, e); + *off += len; + } + if(*off > maxt) maxt = *off; + freeev(e); + break; + } + if(ronly && !rela){ + error(lineno, "only relative addressing allowed"); + rela = 1; + } + if(peek() == SYM){ + next(); + sy = getsym(sname); + if(sy->t == SYMFREE) + error(lineno, "undefined %s", sy->name); + else + f *= sy->e; + } + if(rela) + *off += f; + else + *off = f; + if(peek() == '['){ + next(); + sy = getsym(sname); + if(sy->t != SYMFREE && sy->t != SYMNUM) + error(lineno, "already defined %s", sy->name); + else{ + sy->t = SYMNUM; + sy->e = *off; + } + expect(']'); + } + if(*off < mint) mint = *off; + if(*off > maxt) maxt = *off; + rela = 0; + break; + } + +} + +static void +signal(char *st) +{ + Signal *s; + double off; + + s = emalloc(sizeof(Signal)); + s->name = strdup(st); + s->ev = events(&off, 0); + expect(';'); + *siglast = s; + siglast = &s->next; + nsig++; +} + +static void +slantfill(double x1, double x2, double t, double b, double tw) +{ + double x; + double sw = 0.05; + double soff = 0.05; + + for(x = x1; x < x2 - sw; x += soff) + print("line from %g,%g to %g,%g\n", x, t, x+sw, b); + if(x < x2) + print("line from %g,%g to %g,%g\n", x, t, (2*sw*tw+2*tw*x+sw*x2)/(sw+2*tw), (sw*t+t*(x-x2)+b*(2*tw-x+x2))/(sw+2*tw)); +} + +static void +sigout(Signal *s, double top) +{ + Event *e, *n; + double l, w, t, b, x1, x2, tw, m; + + for(e = s->ev; e != nil && e->next != nil && e->next->t < left; e = e->next) + ; + if(e == nil) + return; + b = top - rheight * 0.75; + t = top - rheight * 0.25; + tw = width * 0.003; + m = (t+b)/2; + l = width * 0.2; + w = width * 0.8 / (right - left); + x1 = l; + print("\"%s\" ljust at %g,%g\n", s->name, width * 0.1, m); + while(n = e->next, n != nil && n->t < right){ + x2 = (n->t - left) * w + l; + if(n->line) + print("line from %g,%g to %g,%g dashed\n", x2, 0.0, x2, nsig*rheight); + switch(e->val){ + case VZ: + print("line from %g,%g to %g,%g\n", x1, m, x2, m); + break; + case VL: + print("line from %g,%g to %g,%g\n", x1, b, x2-tw, b); + print("line from %g,%g to %g,%g\n", x2-tw, b, x2, m); + break; + case VH: + print("line from %g,%g to %g,%g\n", x1, t, x2-tw, t); + print("line from %g,%g to %g,%g\n", x2-tw, t, x2, m); + break; + case VMULT: + print("\"%s\" at %g,%g\n", e->data, (x1+x2)/2, m); + if(0){ + case VX: + slantfill(x1, x2-tw, t, b, tw); + } + print("line from %g,%g to %g,%g\n", x1, b, x2-tw, b); + print("line from %g,%g to %g,%g\n", x2-tw, b, x2, m); + print("line from %g,%g to %g,%g\n", x1, t, x2-tw, t); + print("line from %g,%g to %g,%g\n", x2-tw, t, x2, m); + break; + default: + fprint(2, "unknown event type %d\n", e->val); + } + switch(n->val){ + case VL: + print("line from %g,%g to %g,%g\n", x2, m, x2+tw, b); + break; + case VH: + print("line from %g,%g to %g,%g\n", x2, m, x2+tw, t); + break; + case VMULT: + case VX: + print("line from %g,%g to %g,%g\n", x2, m, x2+tw, b); + print("line from %g,%g to %g,%g\n", x2, m, x2+tw, t); + break; + } + e = e->next; + if(e->val == VZ) + x1 = x2; + else + x1 = x2 + tw; + } + x2 = (right - left) * w + l; + switch(e->val){ + case VZ: + print("line from %g,%g to %g,%g\n", x1, m, x2, m); + break; + case VL: + print("line from %g,%g to %g,%g\n", x1, b, x2, b); + break; + case VH: + print("line from %g,%g to %g,%g\n", x1, t, x2, t); + break; + case VMULT: + print("\"%s\" at %g,%g\n", e->data, (x1+x2)/2, m); + if(0){ + case VX: + slantfill(x1, x2, t, b, tw); + } + print("line from %g,%g to %g,%g\n", x1, b, x2, b); + print("line from %g,%g to %g,%g\n", x1, t, x2, t); + break; + default: + fprint(2, "unknown event type %d\n", e->val); + } +} + +static void +parseopts(char *l) +{ + char *f[3]; + int rc; + + rc = tokenize(l, f, nelem(f)); + if(rc != 3){ + error(lineno, ".TPS wrong syntax"); + return; + } + width = strtod(f[1], 0); + rheight = strtod(f[2], 0); +} + +static void +cleansym(void) +{ + Symbol **p, *s, *sn; + Signal *si, *sin; + Event *e, *en; + + for(p = stab; p < stab + nelem(stab); p++) + for(s = *p, *p = nil; s != nil; s = sn){ + + free(s->name); + sn = s->next; + free(s); + } + memset(stab, 0, sizeof(stab)); + + for(si = sigfirst; si != nil; si = sin){ + for(e = si->ev; e != nil; e = en){ + en = e->next; + free(e); + } + free(si->name); + sin = si->next; + free(si); + } + siglast = &sigfirst; +} + +static void +diagram(char *l) +{ + Symbol *s; + Signal *si; + int t; + double top; + + lastc = 10; + mint = 0; + maxt = 0; + nsig = 0; + parseopts(l); + + for(;;){ + switch(t = next()){ + case SYM: + s = getsym(sname); + if(peek() == '=') + assign(s); + else + signal(s->name); + break; + case CMD: + if(strcmp(sname, "TPE") == 0) + goto end; + error(lineno, "unknown command %s", sname); + break; + default: + error(lineno, "unexpected %T", t); + } + } +end: + + print(".PS %g %g\n", width, rheight * nsig); + left = mint; + right = maxt; + top = rheight * nsig; + for(si = sigfirst; si != nil; si = si->next, top -= rheight) + sigout(si, top); + print(".PE\n"); + + cleansym(); +} + +static void +run(char *f) +{ + char *l; + + if(f == nil) + bp = Bfdopen(0, OREAD); + else + bp = Bopen(f, OREAD); + if(bp == nil) + sysfatal("open: %r"); + Blethal(bp, nil); + lineno = 1; + + for(;;){ + l = Brdstr(bp, '\n', 1); + if(l == nil) + break; + lineno++; + if(strncmp(l, ".TPS", 4) == 0 && (!l[4] || isspace(l[4]))) + diagram(l); + else + print("%s\n", l); + free(l); + } + Bterm(bp); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [ files ]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int i; + + fmtinstall('T', Tfmt); + + ARGBEGIN { + default: usage(); + } ARGEND; + + if(argc == 0) + run(nil); + else + for(i = 0; i < argc; i++) + run(argv[i]); + + exits(nil); +}