8 typedef struct Operator Operator;
9 typedef struct Token Token;
10 typedef struct Constant Constant;
11 typedef struct Code Code;
48 void add(void) { sp--; *sp += *(sp+1); }
49 void sub(void) { sp--; *sp -= *(sp+1); }
50 void mul(void) { sp--; *sp *= *(sp+1); }
51 void div(void) { sp--; *sp /= *(sp+1); }
52 void mod(void) { sp--; *sp = fmod(*sp, *(sp+1)); }
53 void pot(void) { sp--; *sp = pow(*sp, *(sp+1)); }
54 void osin(void) { *sp = sin(*sp); }
55 void ocos(void) { *sp = cos(*sp); }
56 void otan(void) { *sp = tan(*sp); }
57 void oasin(void) { *sp = asin(*sp); }
58 void oacos(void) { *sp = acos(*sp); }
59 void oatan(void) { *sp = atan(*sp); }
60 void osqrt(void) { *sp = sqrt(*sp); }
61 void oexp(void) { *sp = exp(*sp); }
62 void olog(void) { *sp = log10(*sp); }
63 void oln(void) { *sp = log(*sp); }
72 "+", OBINARY, 0, 0, add,
73 "-", OBINARY, 0, 0, sub,
74 "*", OBINARY, 0, 100, mul,
75 "/", OBINARY, 0, 100, div,
76 "%", OBINARY, 0, 100, mod,
77 "^", OBINARY, 1, 200, pot,
78 "sin", OUNARY, 0, 50, osin,
79 "cos", OUNARY, 0, 50, ocos,
80 "tan", OUNARY, 0, 50, otan,
81 "asin", OUNARY, 0, 50, oasin,
82 "acos", OUNARY, 0, 50, oacos,
83 "atan", OUNARY, 0, 50, oatan,
84 "sqrt", OUNARY, 0, 50, osqrt,
85 "exp", OUNARY, 0, 50, oexp,
86 "log", OUNARY, 0, 50, olog,
87 "ln", OUNARY, 0, 50, oln,
106 double xmin = -10, xmax = 10;
107 double ymin = -10, ymax = 10;
111 int picx = 640, picy = 480;
118 v = mallocz(size, 1);
120 sysfatal("emalloc: %r");
121 setmalloctag(v, getcallerpc(&size));
128 if(c->len >= c->cap) {
130 c->p = realloc(c->p, sizeof(Op) * c->cap);
132 sysfatal("realloc: %r");
140 t->next = opstackbot;
152 sysfatal("stack underflow");
153 opstackbot = t->next;
155 sysfatal("non-operator pop");
156 o.type = t->op->type;
168 opstackbot = t->next;
184 t = emalloc(sizeof(*t));
187 t->val = strtod(*s, s);
205 for(o = ops; o < ops + nelem(ops); o++)
206 if(strncmp(*s, o->s, strlen(o->s)) == 0) {
212 for(c = consts; c < consts + nelem(consts); c++)
213 if(strncmp(*s, c->s, strlen(c->s)) == 0) {
219 sysfatal("syntax error at %s", *s);
224 parse(Code *c, char *s)
243 if(t->op->type == OBINARY)
244 while(opstackbot != nil && opstackbot->type == TOP &&
245 (opstackbot->op->prec > t->op->prec ||
246 t->op->rassoc && opstackbot->op->prec == t->op->prec))
254 while(opstackbot != nil && opstackbot->type == TOP)
256 if(opstackbot == nil)
257 sysfatal("mismatched parentheses");
262 sysfatal("unknown token type %d", t->type);
265 while(opstackbot != nil)
266 switch(opstackbot->type) {
271 sysfatal("mismatched parentheses");
273 sysfatal("syntax error");
285 for(o = c->p; o < c->p + c->len; o++)
287 case ONUMBER: case OVAR:
293 sysfatal("syntax error");
297 sysfatal("syntax error");
303 sysfatal("syntax error");
308 calc(Code *c, double x)
313 for(o = c->p; o < c->p + c->len; o++)
321 case OUNARY: case OBINARY:
328 convx(Rectangle *r, int x)
330 return (xmax - xmin) * (x - r->min.x) / (r->max.x - r->min.x) + xmin;
334 deconvx(Rectangle *r, double dx)
336 return (dx - xmin) * (r->max.x - r->min.x) / (xmax - xmin) + r->min.x + 0.5;
340 convy(Rectangle *r, int y)
342 return (ymax - ymin) * (r->max.y - y) / (r->max.y - r->min.y) + ymin;
346 deconvy(Rectangle *r, double dy)
348 return (ymax - dy) * (r->max.y - r->min.y) / (ymax - ymin) + r->min.y + 0.5;
357 if(x >= picx || y >= picy || x < 0 || y < 0)
359 p = imagedata + (picx * y + x) * 3;
360 p[0] = p[1] = p[2] = 0;
362 draw(screen, Rect(x, y, x + 1, y + 1), color, nil, ZP);
366 drawinter(Code *co, Rectangle *r, double x1, double x2, int n)
372 ix1 = deconvx(r, x1);
373 ix2 = deconvx(r, x2);
377 iy1 = deconvy(r, y1);
382 iy2 = deconvy(r, y2);
385 if(isNaN(y1) || isNaN(y2))
389 if(iy2 >= iy1 - 1 && iy2 <= iy1 + 1)
391 if(iy1 > r->max.y && iy2 > r->max.y)
393 if(iy1 < r->min.y && iy2 < r->min.y)
395 drawinter(co, r, x1, (x1 + x2) / 2, n + 1);
396 drawinter(co, r, (x1 + x2) / 2, x2, n + 1);
400 drawgraph(Code *co, Rectangle *r)
404 for(x = r->min.x; x < r->max.x; x++)
405 drawinter(co, r, convx(r, x), convx(r, x + 1), 0);
413 color = display->black;
414 for(i = 0; i < nfns; i++)
415 drawgraph(&fns[i], &screen->r);
416 flushimage(display, 1);
422 fprint(2, "usage: fplot [-c [-s size]] [-r range] functions ...\n");
431 double xmin_, xmax_, ymin_, ymax_;
435 if(r.min.x == 0 && r.min.y == 0 && r.max.x == 0 && r.max.y == 0)
437 xmin_ = convx(&screen->r, r.min.x);
438 xmax_ = convx(&screen->r, r.max.x);
439 ymin_ = convy(&screen->r, r.max.y);
440 ymax_ = convy(&screen->r, r.min.y);
445 draw(screen, screen->r, display->white, nil, ZP);
450 parsefns(int n, char **s)
456 fns = emalloc(sizeof(*fns) * n);
457 for(i = 0; i < nfns; i++) {
458 parse(&fns[i], s[i]);
459 cur = calcstack(&fns[i]);
463 stack = emalloc(sizeof(*stack) * max);
469 while(*s && !isdigit(*s)) s++;
471 xmin = strtod(s, &s);
472 while(*s && !isdigit(*s)) s++;
474 xmax = strtod(s, &s);
475 while(*s && !isdigit(*s)) s++;
477 ymin = strtod(s, &s);
478 while(*s && !isdigit(*s)) s++;
480 ymax = strtod(s, &s);
486 while(*s && !isdigit(*s)) s++;
488 picx = strtol(s, &s, 0);
489 while(*s && !isdigit(*s)) s++;
491 picy = strtol(s, &s, 0);
495 main(int argc, char **argv)
502 case 'r': parserange(EARGF(usage())); break;
503 case 's': parsesize(EARGF(usage())); break;
504 case 'c': cflag++; break;
509 setfcr(getfcr() & ~(FPZDIV | FPINVAL));
510 parsefns(argc, argv);
512 imagedata = emalloc(picx * picy * 3);
513 memset(imagedata, 0xFF, picx * picy * 3);
514 print("%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, picx, picy);
515 r.min.x = r.min.y = 0;
518 for(i = 0; i < nfns; i++)
519 drawgraph(&fns[i], &r);
520 if(write(1, imagedata, picx * picy * 3) < picx * picy * 3)
521 sysfatal("write: %r");
523 if(initdraw(nil, nil, "fplot") < 0)
524 sysfatal("initdraw: %r");
525 einit(Emouse | Ekeyboard);
531 case 'q': exits(nil);
543 if(getwindow(display, Refnone) < 0)
544 sysfatal("getwindow: %r");