add timepic(1)
This commit is contained in:
parent
4c841d0b46
commit
920783505c
1 changed files with 729 additions and 0 deletions
729
sys/src/cmd/timepic.c
Normal file
729
sys/src/cmd/timepic.c
Normal file
|
@ -0,0 +1,729 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
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);
|
||||
}
|
Loading…
Reference in a new issue