From 5cedca1b69d020c32466f70843a11767773d7e3b Mon Sep 17 00:00:00 2001 From: rsc Date: Sat, 15 May 2004 23:24:00 +0000 Subject: Let's try this. It's BUGGERED. --- src/cmd/grap/coord.c | 71 ++++++ src/cmd/grap/find | 1 + src/cmd/grap/for.c | 89 ++++++++ src/cmd/grap/frame.c | 71 ++++++ src/cmd/grap/grap.h | 236 ++++++++++++++++++++ src/cmd/grap/grap.y | 396 ++++++++++++++++++++++++++++++++++ src/cmd/grap/grapl.lx | 213 ++++++++++++++++++ src/cmd/grap/input.c | 580 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/grap/label.c | 123 +++++++++++ src/cmd/grap/main.c | 164 ++++++++++++++ src/cmd/grap/misc.c | 263 +++++++++++++++++++++++ src/cmd/grap/mkfile | 37 ++++ src/cmd/grap/plot.c | 132 ++++++++++++ src/cmd/grap/print.c | 236 ++++++++++++++++++++ src/cmd/grap/ticks.c | 492 ++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 3104 insertions(+) create mode 100644 src/cmd/grap/coord.c create mode 100644 src/cmd/grap/find create mode 100644 src/cmd/grap/for.c create mode 100644 src/cmd/grap/frame.c create mode 100644 src/cmd/grap/grap.h create mode 100644 src/cmd/grap/grap.y create mode 100644 src/cmd/grap/grapl.lx create mode 100644 src/cmd/grap/input.c create mode 100644 src/cmd/grap/label.c create mode 100644 src/cmd/grap/main.c create mode 100644 src/cmd/grap/misc.c create mode 100644 src/cmd/grap/mkfile create mode 100644 src/cmd/grap/plot.c create mode 100644 src/cmd/grap/print.c create mode 100644 src/cmd/grap/ticks.c (limited to 'src/cmd/grap') diff --git a/src/cmd/grap/coord.c b/src/cmd/grap/coord.c new file mode 100644 index 00000000..72f0fc3a --- /dev/null +++ b/src/cmd/grap/coord.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +char *dflt_coord = "gg"; +char *curr_coord = "gg"; +int ncoord = 0; /* number of explicit coord's given */ + +Point xcoord; +Point ycoord; +int xcflag = 0; /* 1 if xcoord set */ +int ycflag = 0; +int logcoord = 0; + +void coord_x(Point pt) /* remember x coord */ +{ + xcoord = pt; + xcflag = 1; + margin = 0; /* no extra space around picture if explicit coords */ +} + +void coord_y(Point pt) +{ + ycoord = pt; + ycflag = 1; + margin = 0; /* no extra space if explicit coords */ +} + +void coordlog(int n) /* remember log scaling */ +{ + logcoord = n; +} + +void coord(Obj *p) /* set coord range */ +{ + static char buf[10]; + + ncoord++; + if (ncoord > 1 && strcmp(p->name, dflt_coord) == 0) { + /* resetting default coordinate by implication */ + sprintf(buf, "gg%d", ncoord); + dflt_coord = buf; + p = lookup(dflt_coord, 1); + } + if (xcflag) { + p->coord |= XFLAG; + p->pt.x = min(xcoord.x,xcoord.y); /* "xcoord" is xmin, xmax */ + p->pt1.x = max(xcoord.x,xcoord.y); + if ((logcoord&XFLAG) && p->pt.x <= 0.0) + ERROR "can't have log of x coord %g,%g", p->pt.x, p->pt1.x FATAL; + xcflag = 0; + } + if (ycflag) { + p->coord |= YFLAG; + p->pt.y = min(ycoord.x,ycoord.y); /* "ycoord" is ymin, ymax */ + p->pt1.y = max(ycoord.x,ycoord.y); + if ((logcoord&YFLAG) && p->pt.y <= 0.0) + ERROR "can't have log of y coord %g,%g", p->pt.y, p->pt1.y FATAL; + ycflag = 0; + } + p->log = logcoord; + logcoord = 0; + auto_x = 0; +} + +void resetcoord(Obj *p) /* reset current coordinate */ +{ + curr_coord = p->name; +} diff --git a/src/cmd/grap/find b/src/cmd/grap/find new file mode 100644 index 00000000..1c2e905b --- /dev/null +++ b/src/cmd/grap/find @@ -0,0 +1 @@ +exec /usr/bin/egrep -n "$1" *.[chyl] diff --git a/src/cmd/grap/for.c b/src/cmd/grap/for.c new file mode 100644 index 00000000..84de388d --- /dev/null +++ b/src/cmd/grap/for.c @@ -0,0 +1,89 @@ +#include +#include +#include "grap.h" +#include "y.tab.h" + +typedef struct { + Obj *var; /* index variable */ + double to; /* limit */ + double by; + int op; /* operator */ + char *str; /* string to push back */ +} For; + +#define MAXFOR 10 + +For forstk[MAXFOR]; /* stack of for loops */ +For *forp = forstk; /* pointer to current top */ + +void forloop(Obj *var, double from, double to, int op, double by, char *str) /* set up a for loop */ +{ + fprintf(tfd, "# for %s from %g to %g by %c %g \n", + var->name, from, to, op, by); + if (++forp >= forstk+MAXFOR) + ERROR "for loop nested too deep" FATAL; + forp->var = var; + forp->to = to; + forp->op = op; + forp->by = by; + forp->str = str; + setvar(var, from); + nextfor(); + unput('\n'); +} + +void nextfor(void) /* do one iteration of a for loop */ +{ + /* BUG: this should depend on op and direction */ + if (forp->var->fval > SLOP * forp->to) { /* loop is done */ + free(forp->str); + if (--forp < forstk) + ERROR "forstk popped too far" FATAL; + } else { /* another iteration */ + pushsrc(String, "\nEndfor\n"); + pushsrc(String, forp->str); + } +} + +void endfor(void) /* end one iteration of for loop */ +{ + switch (forp->op) { + case '+': + case ' ': + forp->var->fval += forp->by; + break; + case '-': + forp->var->fval -= forp->by; + break; + case '*': + forp->var->fval *= forp->by; + break; + case '/': + forp->var->fval /= forp->by; + break; + } + nextfor(); +} + +char *ifstat(double expr, char *thenpart, char *elsepart) +{ + dprintf("if %g then <%s> else <%s>\n", expr, thenpart, elsepart? elsepart : ""); + if (expr) { + unput('\n'); + pushsrc(Free, thenpart); + pushsrc(String, thenpart); + unput('\n'); + if (elsepart) + free(elsepart); + return thenpart; /* to be freed later */ + } else { + free(thenpart); + if (elsepart) { + unput('\n'); + pushsrc(Free, elsepart); + pushsrc(String, elsepart); + unput('\n'); + } + return elsepart; + } +} diff --git a/src/cmd/grap/frame.c b/src/cmd/grap/frame.c new file mode 100644 index 00000000..a00a9d8d --- /dev/null +++ b/src/cmd/grap/frame.c @@ -0,0 +1,71 @@ +#include +#include +#include "grap.h" +#include "y.tab.h" + +double frame_ht; /* default frame height */ +double frame_wid; /* and width */ + +int nsides = 0; /* how many sides given on this frame */ +char *sides[] = { + "\tline from Frame.nw to Frame.ne", + "\tline from Frame.sw to Frame.se", + "\tline from Frame.sw to Frame.nw", + "\tline from Frame.se to Frame.ne" +}; +char *newsides[4] = { 0, 0, 0, 0 }; /* filled in later */ + +void frame(void) /* pump out frame definition, reset for next */ +{ + int i; + + fprintf(tfd, "\tframeht = %g\n", frame_ht); + fprintf(tfd, "\tframewid = %g\n", frame_wid); + fprintf(tfd, "Frame:\tbox ht frameht wid framewid with .sw at 0,0 "); + if (nsides == 0) + fprintf(tfd, "\n"); + else { + fprintf(tfd, "invis\n"); + for (i = 0; i < 4; i++) { + if (newsides[i]) { + fprintf(tfd, "%s\n", newsides[i]); + free(newsides[i]); + newsides[i] = 0; + } else + fprintf(tfd, "%s\n", sides[i]); + } + nsides = 0; + } +} + +void frameht(double f) /* set height of frame */ +{ + frame_ht = f; +} + +void framewid(double f) /* set width of frame */ +{ + frame_wid = f; +} + +void frameside(int type, Attr *desc) /* create and remember sides */ +{ + int n; + char buf[100]; + + nsides++; + switch (type) { + case 0: /* no side specified; kludge up all */ + frameside(TOP, desc); + frameside(BOT, desc); + frameside(LEFT, desc); + frameside(RIGHT, desc); + return; + case TOP: n = 0; break; + case BOT: n = 1; break; + case LEFT: n = 2; break; + case RIGHT: n = 3; break; + } + sprintf(buf, "%s %s", sides[n], desc_str(desc)); + newsides[n] = tostring(buf); +} diff --git a/src/cmd/grap/grap.h b/src/cmd/grap/grap.h new file mode 100644 index 00000000..65c7f83f --- /dev/null +++ b/src/cmd/grap/grap.h @@ -0,0 +1,236 @@ +extern char errbuf[200]; +#define ERROR sprintf(errbuf, +#define FATAL ), yyerror(errbuf), exit(1) +#define WARNING ), yyerror(errbuf) + +#define dprintf if(dbg)printf + +#define String 01 +#define Macro 02 +#define File 04 +#define Char 010 +#define Thru 020 +#define Free 040 + +#define MARGIN 0.07 /* default margin around data */ +#define SLOP 1.001 /* slop for limits of for loops */ +#define FRAMEWID 3 /* default width for boxes and ellipses */ +#define FRAMEHT 2 /* default height and line length */ +#define TICKLEN 0.1 + +#define MAXNUM 200 + +#define XFLAG 01 +#define YFLAG 02 + +#define INTICK 01 +#define OUTICK 02 + +#define BOT 01 +#define TOP 02 +#define RIGHT 04 +#define LEFT 010 + +#define RJUST 01 +#define LJUST 02 +#define ABOVE 04 +#define BELOW 010 + +typedef struct infile { + FILE *fin; + char *fname; + int lineno; +} Infile; + +typedef struct { /* input source */ + int type; /* Macro, String, File */ + char *sp; /* if String or Macro */ +} Src; + +extern Src src[], *srcp; /* input source stack */ + +#define MAXARGS 100 +typedef struct { /* argument stack */ + char *argstk[MAXARGS]; /* pointers to args */ + char *argval; /* points to space containing args */ +} Arg; + +extern Infile infile[10]; +extern Infile *curfile; + +typedef struct { + struct obj *obj; + double x, y; +} Point; + +typedef struct attr { /* e.g., DASH 1.1 or "..." rjust size *.5 */ + int type; + double fval; + char *sval; + int just; /* justification, for STRING type */ + int op; /* optional operator, ditto */ + struct attr *next; +} Attr; + +typedef struct obj { /* a name and its properties */ + char *name; + char *val; /* body of define, etc. */ + double fval; /* if a numeric variable */ + Point pt; /* usually for max and min */ + Point pt1; + int type; /* NAME, DEFNAME, ... */ + int first; /* 1 after 1st item seen */ + int coord; /* 1 if coord system specified for this name */ + int log; /* x, y, or z (= x+y) */ + Attr *attr; /* DASH, etc., for now */ + struct obj *next; +} Obj; + +typedef union { /* the yacc stack type */ + int i; + char *p; + double f; + Point pt; + Obj *op; + Attr *ap; +} YYSTYPE; + +extern YYSTYPE yylval, yyval; + +extern int dbg; + +extern int ntext; +extern double num[MAXNUM]; +extern int nnum; +extern int ntick, tside; + +extern char *tostring(char *); +extern char *grow(char *, char *, int, int); + +extern int lineno; +extern int synerr; +extern int codegen; +extern char tempfile[]; +extern FILE *tfd; + +extern Point ptmin, ptmax; + +extern char *dflt_coord; +extern char *curr_coord; +extern int ncoord; +extern int auto_x; +extern double margin; +extern int autoticks; +extern int pointsize, ps_set; + + +#define logit(x) (x) = log10(x) +#define Log10(x) errcheck(log10(x), "log") +#define Exp(x) errcheck(exp(x), "exp") +#define Sqrt(x) errcheck(sqrt(x), "sqrt") + +#define min(x,y) (((x) <= (y)) ? (x) : (y)) +#define max(x,y) (((x) >= (y)) ? (x) : (y)) + +extern void yyerror(char *); +extern void coord_x(Point); +extern void coord_y(Point); +extern void coordlog(int); +extern void coord(Obj *); +extern void resetcoord(Obj *); +extern void savenum(int, double); +extern void setjust(int); +extern void setsize(int, double); +extern void range(Point); +extern void halfrange(Obj *, int, double); +extern Obj *lookup(char *, int); +extern double getvar(Obj *); +extern double setvar(Obj *, double); +extern Point makepoint(Obj *, double, double); +extern Attr *makefattr(int, double); +extern Attr *makesattr(char *); +extern Attr *makeattr(int, double, char *, int, int); +extern Attr *addattr(Attr *, Attr *); +extern void freeattr(Attr *); +extern char *slprint(Attr *); +extern char *juststr(int); +extern char *sprntf(char *, Attr *); +extern void forloop(Obj *, double, double, int, double, char *); +extern void nextfor(void); +extern void endfor(void); +extern char *ifstat(double, char *, char *); +extern void frame(void); +extern void frameht(double); +extern void framewid(double); +extern void frameside(int, Attr *); +extern void pushsrc(int, char *); +extern void popsrc(void); +extern void definition(char *); +extern char *delimstr(char *); +extern int baldelim(int, char *); +extern void dodef(Obj *); +extern int getarg(char *); +extern int input(void); +extern int nextchar(void); +extern void do_thru(void); +extern int unput(int); +extern void pbstr(char *); +extern double errcheck(double, char *); +extern void yyerror(char *); +extern void eprint(void); +extern int yywrap(void); +extern void copyfile(char *); +extern void copydef(Obj *); +extern Obj *copythru(char *); +extern char *addnewline(char *); +extern void copyuntil(char *); +extern void copy(void); +extern void shell_init(void); +extern void shell_text(char *); +extern void shell_exec(void); +extern void labelwid(double); +extern void labelmove(int, double); +extern void label(int, Attr *); +extern void lab_adjust(void); +extern char *sizeit(Attr *); +extern void line(int, Point, Point, Attr *); +extern void circle(double, Point); +extern char *xyname(Point); +extern void pic(char *); +extern void numlist(void); +extern void plot(Attr *, Point); +extern void plotnum(double, char *, Point); +extern void drawdesc(int, Obj *, Attr *, char *); +extern void next(Obj *, Point, Attr *); +extern void print(void); +extern void endstat(void); +extern void graph(char *); +extern void setup(void); +extern void do_first(void); +extern void reset(void); +extern void opentemp(void); +extern void savetick(double, char *); +extern void dflt_tick(double); +extern void tickside(int); +extern void tickoff(int); +extern void gridtickoff(void); +extern void setlist(void); +extern void tickdir(int, double, int); +extern void ticks(void); +extern double modfloor(double, double); +extern double modceil(double, double); +extern void do_autoticks(Obj *); +extern void logtick(double, double, double); +extern Obj *setauto(void); +extern void autoside(Obj *, int); +extern void autolog(Obj *, int); +extern void iterator(double, double, int, double, char *); +extern void ticklist(Obj *, int); +extern void print_ticks(int, int, Obj *, char *, char *); +extern void maketick(int, char *, int, int, double, char *, char *, char *); +extern void griddesc(Attr *); +extern void gridlist(Obj *); +extern char *desc_str(Attr *); +extern int sidelog(int, int); + +extern Obj *objlist; diff --git a/src/cmd/grap/grap.y b/src/cmd/grap/grap.y new file mode 100644 index 00000000..6d96b2f1 --- /dev/null +++ b/src/cmd/grap/grap.y @@ -0,0 +1,396 @@ +%{ +#include +#include +#include +#include +#include "grap.h" + +//#define RAND_MAX 32767 /* if your rand() returns bigger, change this too */ + +extern int yylex(void); +extern int yyparse(void); + +%} + +%token FRAME TICKS GRID LABEL COORD +%token LINE ARROW CIRCLE DRAW NEW PLOT NEXT +%token

PIC +%token COPY THRU UNTIL +%token FOR FROM TO BY AT WITH +%token IF +%token

GRAPH THEN ELSE DOSTR +%token DOT DASH INVIS SOLID +%token TEXT JUST SIZE +%token LOG EXP SIN COS ATAN2 SQRT RAND MAX MIN INT PRINT SPRINTF +%token X Y SIDE IN OUT OFF UP DOWN ACROSS +%token HEIGHT WIDTH RADIUS +%token NUMBER +%token NAME VARNAME DEFNAME +%token

STRING +%token ST '(' ')' ',' + +%right '=' +%left OR +%left AND +%nonassoc GT LT LE GE EQ NE +%left '+' '-' +%left '*' '/' '%' +%right UMINUS NOT +%right '^' + +%type expr optexpr if_expr number assign +%type optop +%type

optstring if +%type optname iterator name +%type point +%type side optside numlist comma linetype drawtype +%type linedesc optdesc stringlist string stringattr sattrlist exprlist +%type frameitem framelist coordlog +%type string_expr + +%% + +top: + graphseq { if (codegen && !synerr) graph((char *) 0); } + | /* empty */ { codegen = 0; } + | error { codegen = 0; ERROR "syntax error" WARNING; } + ; + +graphseq: + statlist + | graph statlist + | graphseq graph statlist + ; +graph: + GRAPH { graph($1); endstat(); } + ; + +statlist: + ST + | stat ST { endstat(); } + | statlist stat ST { endstat(); } + ; + +stat: + FRAME framelist { codegen = 1; } + | ticks { codegen = 1; } + | grid { codegen = 1; } + | label { codegen = 1; } + | coord + | plot { codegen = 1; } + | line { codegen = 1; } + | circle { codegen = 1; } + | draw + | next { codegen = 1; } + | PIC { codegen = 1; pic($1); } + | for + | if + | copy + | numlist { codegen = 1; numlist(); } + | assign + | PRINT expr { fprintf(stderr, "\t%g\n", $2); } + | PRINT string { fprintf(stderr, "\t%s\n", $2->sval); freeattr($2); } + | /* empty */ + ; + +numlist: + number { savenum(0, $1); $$ = 1; } + | numlist number { savenum($1, $2); $$ = $1+1; } + | numlist comma number { savenum($1, $3); $$ = $1+1; } + ; +number: + NUMBER + | '-' NUMBER %prec UMINUS { $$ = -$2; } + | '+' NUMBER %prec UMINUS { $$ = $2; } + ; + +label: + LABEL optside stringlist lablist { label($2, $3); } + ; +lablist: + labattr + | lablist labattr + | /* empty */ + ; +labattr: + UP expr { labelmove($1, $2); } + | DOWN expr { labelmove($1, $2); } + | SIDE expr { labelmove($1, $2); /* LEFT or RIGHT only */ } + | WIDTH expr { labelwid($2); } + ; + +framelist: + framelist frameitem + | /* empty */ { $$ = 0; } + ; +frameitem: + HEIGHT expr { frameht($2); } + | WIDTH expr { framewid($2); } + | side linedesc { frameside($1, $2); } + | linedesc { frameside(0, $1); } + ; +side: + SIDE + ; +optside: + side + | /* empty */ { $$ = 0; } + ; + +linedesc: + linetype optexpr { $$ = makeattr($1, $2, (char *) 0, 0, 0); } + ; +linetype: + DOT | DASH | SOLID | INVIS + ; +optdesc: + linedesc + | /* empty */ { $$ = makeattr(0, 0.0, (char *) 0, 0, 0); } + ; + +ticks: + TICKS tickdesc { ticks(); } + ; +tickdesc: + tickattr + | tickdesc tickattr + ; +tickattr: + side { tickside($1); } + | IN expr { tickdir(IN, $2, 1); } + | OUT expr { tickdir(OUT, $2, 1); } + | IN { tickdir(IN, 0.0, 0); } + | OUT { tickdir(OUT, 0.0, 0); } + | AT optname ticklist { setlist(); ticklist($2, AT); } + | iterator { setlist(); ticklist($1, AT); } + | side OFF { tickoff($1); } + | OFF { tickoff(LEFT|RIGHT|TOP|BOT); } + | labattr + ; +ticklist: + tickpoint + | ticklist comma tickpoint + ; +tickpoint: + expr { savetick($1, (char *) 0); } + | expr string { savetick($1, $2->sval); } + ; +iterator: + FROM optname expr TO optname expr BY optop expr optstring + { iterator($3, $6, $8, $9, $10); $$ = $2; } + | FROM optname expr TO optname expr optstring + { iterator($3, $6, '+', 1.0, $7); $$ = $2; } + ; +optop: + '+' { $$ = '+'; } + | '-' { $$ = '-'; } + | '*' { $$ = '*'; } + | '/' { $$ = '/'; } + | /* empty */ { $$ = ' '; } + ; +optstring: + string { $$ = $1->sval; } + | /* empty */ { $$ = (char *) 0; } + ; + +grid: + GRID griddesc { ticks(); } + ; +griddesc: + gridattr + | griddesc gridattr + ; +gridattr: + side { tickside($1); } + | X { tickside(BOT); } + | Y { tickside(LEFT); } + | linedesc { griddesc($1); } + | AT optname ticklist { setlist(); gridlist($2); } + | iterator { setlist(); gridlist($1); } + | TICKS OFF { gridtickoff(); } + | OFF { gridtickoff(); } + | labattr + ; + +line: + LINE FROM point TO point optdesc { line($1, $3, $5, $6); } + | LINE optdesc FROM point TO point { line($1, $4, $6, $2); } + ; +circle: + CIRCLE RADIUS expr AT point { circle($3, $5); } + | CIRCLE AT point RADIUS expr { circle($5, $3); } + | CIRCLE AT point { circle(0.0, $3); } + ; + +stringlist: + string + | stringlist string { $$ = addattr($1, $2); } + ; +string: + STRING sattrlist { $$ = makesattr($1); } + | SPRINTF '(' STRING ')' sattrlist + { $$ = makesattr(sprntf($3, (Attr*) 0)); } + | SPRINTF '(' STRING ',' exprlist ')' sattrlist + { $$ = makesattr(sprntf($3, $5)); } + ; +exprlist: + expr { $$ = makefattr(NUMBER, $1); } + | exprlist ',' expr { $$ = addattr($1, makefattr(NUMBER, $3)); } + ; +sattrlist: + stringattr + | sattrlist stringattr + | /* empty */ { $$ = (Attr *) 0; } + ; +stringattr: + JUST { setjust($1); } + | SIZE optop expr { setsize($2, $3); } + ; + +coord: + COORD optname coordlist { coord($2); } + | COORD optname { resetcoord($2); } + ; +coordlist: + coorditem + | coordlist coorditem + ; +coorditem: + coordlog { coordlog($1); } + | X point { coord_x($2); } + | Y point { coord_y($2); } + | X optname expr TO expr { coord_x(makepoint($2, $3, $5)); } + | Y optname expr TO expr { coord_y(makepoint($2, $3, $5)); } + | X FROM optname expr TO expr { coord_x(makepoint($3, $4, $6)); } + | Y FROM optname expr TO expr { coord_y(makepoint($3, $4, $6)); } + ; +coordlog: + LOG X { $$ = XFLAG; } + | LOG Y { $$ = YFLAG; } + | LOG X LOG Y { $$ = XFLAG|YFLAG; } + | LOG Y LOG X { $$ = XFLAG|YFLAG; } + | LOG LOG { $$ = XFLAG|YFLAG; } + ; + +plot: + stringlist AT point { plot($1, $3); } + | PLOT stringlist AT point { plot($2, $4); } + | PLOT expr optstring AT point { plotnum($2, $3, $5); } + ; + +draw: + drawtype optname linedesc { drawdesc($1, $2, $3, (char *) 0); } + | drawtype optname optdesc string { drawdesc($1, $2, $3, $4->sval); } + | drawtype optname string optdesc { drawdesc($1, $2, $4, $3->sval); } + ; +drawtype: + DRAW + | NEW + ; + +next: + NEXT optname AT point optdesc { next($2, $4, $5); } + +copy: + COPY copylist { copy(); } + ; +copylist: + copyattr + | copylist copyattr + ; +copyattr: + string { copyfile($1->sval); } + | THRU DEFNAME { copydef($2); } + | UNTIL string { copyuntil($2->sval); } + ; + +for: + FOR name FROM expr TO expr BY optop expr DOSTR + { forloop($2, $4, $6, $8, $9, $10); } + | FOR name FROM expr TO expr DOSTR + { forloop($2, $4, $6, '+', 1.0, $7); } + | FOR name '=' expr TO expr BY optop expr DOSTR + { forloop($2, $4, $6, $8, $9, $10); } + | FOR name '=' expr TO expr DOSTR + { forloop($2, $4, $6, '+', 1.0, $7); } + ; + +if: + IF if_expr THEN ELSE { $$ = ifstat($2, $3, $4); } + | IF if_expr THEN { $$ = ifstat($2, $3, (char *) 0); } + ; +if_expr: + expr + | string_expr + | if_expr AND string_expr { $$ = $1 && $3; } + | if_expr OR string_expr { $$ = $1 || $3; } + ; +string_expr: + STRING EQ STRING { $$ = strcmp($1,$3) == 0; free($1); free($3); } + | STRING NE STRING { $$ = strcmp($1,$3) != 0; free($1); free($3); } + ; + +point: + optname expr comma expr { $$ = makepoint($1, $2, $4); } + | optname '(' expr comma expr ')' { $$ = makepoint($1, $3, $5); } + ; +comma: + ',' { $$ = ','; } + ; + +optname: + NAME { $$ = $1; } + | /* empty */ { $$ = lookup(curr_coord, 1); } + ; + +expr: + NUMBER + | assign + | '(' string_expr ')' { $$ = $2; } + | VARNAME { $$ = getvar($1); } + | expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { if ($3 == 0.0) { + ERROR "division by 0" WARNING; $3 = 1; } + $$ = $1 / $3; } + | expr '%' expr { if ((long)$3 == 0) { + ERROR "mod division by 0" WARNING; $3 = 1; } + $$ = (long)$1 % (long)$3; } + | '-' expr %prec UMINUS { $$ = -$2; } + | '+' expr %prec UMINUS { $$ = $2; } + | '(' expr ')' { $$ = $2; } + | LOG '(' expr ')' { $$ = Log10($3); } + | EXP '(' expr ')' { $$ = Exp($3 * log(10.0)); } + | expr '^' expr { $$ = pow($1, $3); } + | SIN '(' expr ')' { $$ = sin($3); } + | COS '(' expr ')' { $$ = cos($3); } + | ATAN2 '(' expr ',' expr ')' { $$ = atan2($3, $5); } + | SQRT '(' expr ')' { $$ = Sqrt($3); } + | RAND '(' ')' { $$ = (double)rand() / (double)RAND_MAX; } + | MAX '(' expr ',' expr ')' { $$ = $3 >= $5 ? $3 : $5; } + | MIN '(' expr ',' expr ')' { $$ = $3 <= $5 ? $3 : $5; } + | INT '(' expr ')' { $$ = (long) $3; } + | expr GT expr { $$ = $1 > $3; } + | expr LT expr { $$ = $1 < $3; } + | expr LE expr { $$ = $1 <= $3; } + | expr GE expr { $$ = $1 >= $3; } + | expr EQ expr { $$ = $1 == $3; } + | expr NE expr { $$ = $1 != $3; } + | expr AND expr { $$ = $1 && $3; } + | expr OR expr { $$ = $1 || $3; } + | NOT expr { $$ = !($2); } + ; +assign: + name '=' expr { $$ = setvar($1, $3); } + ; + +name: + NAME + | VARNAME + ; + +optexpr: + expr + | /* empty */ { $$ = 0.0; } + ; diff --git a/src/cmd/grap/grapl.lx b/src/cmd/grap/grapl.lx new file mode 100644 index 00000000..0023aded --- /dev/null +++ b/src/cmd/grap/grapl.lx @@ -0,0 +1,213 @@ +%Start A str def thru sh + +%{ +#undef input +#undef unput +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +extern struct symtab symtab[]; + +int yyback(int *, int); +int yylook(void); +int yywrap(void); +void shell_init(void), shell_exec(void), shell_text(char *); + +#define CADD cbuf[clen++] = yytext[0]; \ + if (clen >= CBUFLEN-1) { \ + ERROR "string too long", cbuf WARNING; BEGIN A; } +#define CBUFLEN 1500 +char cbuf[CBUFLEN]; +int clen, cflag; +int c, delim, shcnt; +%} + +A [a-zA-Z_] +B [a-zA-Z0-9_] +D [0-9] +WS [ \t] + +%% + if (yybgin-yysvec-1 == 0) { /* witchcraft */ + BEGIN A; + } + +{WS} ; +"\\"\n ; +\n return(ST); +";" return(ST); + +line return(yylval.i = LINE); +arrow { yylval.i = ARROW; return(LINE); } +circle return(yylval.i = CIRCLE); +frame return(FRAME); +tick(s)? return(TICKS); +grid(line)?(s)? return(GRID); +coord(s)? return(COORD); +log return(LOG); +exp return(EXP); +sin return(SIN); +cos return(COS); +atan2 return(ATAN2); +sqrt return(SQRT); +rand return(RAND); +max return(MAX); +min return(MIN); +int return(INT); +print return(PRINT); +sprintf return(SPRINTF); +pic{WS}.* { yylval.p = tostring(yytext+3); return(PIC); } +graph{WS}.* { yylval.p = tostring(yytext+5); return(GRAPH); } + +for return(FOR); +^Endfor\n { endfor(); } +do { yylval.p = delimstr("loop body"); BEGIN A; return(DOSTR); } + +copy|include { return(COPY); } +thru|through { BEGIN thru; return(THRU); } +{WS}+ ; +{A}{B}*|. { yylval.op = copythru(yytext); BEGIN A; return(DEFNAME); } +until return(UNTIL); + +if return(IF); +then { yylval.p = delimstr("then part"); BEGIN A; return(THEN); } +else { yylval.p = delimstr("else part"); BEGIN A; return(ELSE); } + +next return(NEXT); +draw return(yylval.i = DRAW); +new return(yylval.i = NEW); +plot return(yylval.i = PLOT); +label(s)? return(LABEL); +x return(X); +y return(Y); + +top { yylval.i = TOP; return SIDE; } +bot(tom)? { yylval.i = BOT; return SIDE; } +left { yylval.i = LEFT; return SIDE; } +right { yylval.i = RIGHT; return SIDE; } +up return(yylval.i = UP); +down return(yylval.i = DOWN); +across return(yylval.i = ACROSS); +height|ht return(yylval.i = HEIGHT); +wid(th)? return(yylval.i = WIDTH); +rad(ius)? return(yylval.i = RADIUS); +invis return(yylval.i = INVIS); +dot(ted) return(yylval.i = DOT); +dash(ed) return(yylval.i = DASH); +solid return(yylval.i = SOLID); + +ljust { yylval.i = LJUST; return JUST; } +rjust { yylval.i = RJUST; return JUST; } +above { yylval.i = ABOVE; return JUST; } +below { yylval.i = BELOW; return JUST; } +size return(yylval.i = SIZE); + +from return(yylval.i = FROM); +to return(yylval.i = TO); +by|step return(yylval.i = BY); +at return(yylval.i = AT); +with return(yylval.i = WITH); +in return(yylval.i = IN); +out return(yylval.i = OUT); +off return(yylval.i = OFF); + +sh{WS}+ { BEGIN sh; + if ((delim = input()) == '{') { + shcnt = 1; + delim = '}'; + } + shell_init(); + } +{A}{B}* { + int c; + Obj *p; + if (yytext[0] == delim) { + shell_exec(); + BEGIN A; + } else { + p = lookup(yytext, 0); + if (p != NULL && p->type == DEFNAME) { + c = input(); + unput(c); + if (c == '(') + dodef(p); + else + pbstr(p->val); + } else + shell_text(yytext); + } + } +"{" { shcnt++; shell_text(yytext); } +"}" { if (delim != '}' || --shcnt > 0) + shell_text(yytext); + else { + shell_exec(); + BEGIN A; + } + } +.|\n { if (yytext[0] == delim) { + shell_exec(); + BEGIN A; + } else + shell_text(yytext); + } + +define{WS}+ { BEGIN def; } +{A}{B}* { definition(yytext); BEGIN A; } + +({D}+("."?){D}*|"."{D}+)((e|E)("+"|-)?{D}+)?i? { + yylval.f = atof(yytext); return(NUMBER); } + +^"."[^0-9].* { if (yytext[1] == 'G' && yytext[2] == '2') { + yylval.i = yytext[2]; + return(EOF); + } else { + yylval.p = tostring(yytext); + return(PIC); + } + } + +{A}{B}* { + int c; + Obj *p; + p = lookup(yytext, 1); + if (p->type == DEFNAME) { + c = input(); + unput(c); + if (c == '(') /* it's name(...) */ + dodef(p); + else /* no argument list */ + pbstr(p->val); + } else { + yylval.op = p; + return p->type; /* NAME or VARNAME */ + } + } + +"==" return(EQ); +">=" return(GE); +"<=" return(LE); +"!=" return(NE); +">" return(GT); +"<" return(LT); +"&&" return(AND); +"||" return(OR); +"!" return(NOT); + +\" { BEGIN str; clen = 0; } + +#.* ; + +. { yylval.i = yytext[0]; return(yytext[0]); } + +\" { BEGIN A; cbuf[clen] = 0; + yylval.p = tostring(cbuf); return(STRING); } +\n { ERROR "newline in string" WARNING; BEGIN A; return(ST); } +"\\\"" { cbuf[clen++] = '\\'; cbuf[clen++] = '"'; } +"\\\\" { cbuf[clen++] = '\\'; cbuf[clen++] = '\\'; } +. { CADD; } + +%% diff --git a/src/cmd/grap/input.c b/src/cmd/grap/input.c new file mode 100644 index 00000000..f558145e --- /dev/null +++ b/src/cmd/grap/input.c @@ -0,0 +1,580 @@ +#include +#include +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +Infile infile[10]; +Infile *curfile = infile; + +#define MAXSRC 50 +Src src[MAXSRC]; /* input source stack */ +Src *srcp = src; + +void pushsrc(int type, char *ptr) /* new input source */ +{ + if (++srcp >= src + MAXSRC) + ERROR "inputs nested too deep" FATAL; + srcp->type = type; + srcp->sp = ptr; + if (dbg) { + printf("\n%3d ", srcp - src); + switch (srcp->type) { + case File: + printf("push file %s\n", ((Infile *)ptr)->fname); + break; + case Macro: + printf("push macro <%s>\n", ptr); + break; + case Char: + printf("push char <%c>\n", *ptr); + break; + case Thru: + printf("push thru\n"); + break; + case String: + printf("push string <%s>\n", ptr); + break; + case Free: + printf("push free <%s>\n", ptr); + break; + default: + ERROR "pushed bad type %d", srcp->type FATAL; + } + } +} + +void popsrc(void) /* restore an old one */ +{ + if (srcp <= src) + ERROR "too many inputs popped" FATAL; + if (dbg) { + printf("%3d ", srcp - src); + switch (srcp->type) { + case File: + printf("pop file\n"); + break; + case Macro: + printf("pop macro\n"); + break; + case Char: + printf("pop char <%c>\n", *srcp->sp); + break; + case Thru: + printf("pop thru\n"); + break; + case String: + printf("pop string\n"); + break; + case Free: + printf("pop free\n"); + break; + default: + ERROR "pop weird input %d", srcp->type FATAL; + } + } + srcp--; +} + +void definition(char *s) /* collect definition for s and install */ + /* definitions picked up lexically */ +{ + char *p; + Obj *stp; + + p = delimstr("definition"); + stp = lookup(s, 0); + if (stp != NULL) { /* it's there before */ + if (stp->type != DEFNAME) { + ERROR "%s used as variable and definition", s WARNING; + return; + } + free(stp->val); + } else { + stp = lookup(s, 1); + stp->type = DEFNAME; + } + stp->val = p; + dprintf("installing %s as `%s'\n", s, p); +} + +char *delimstr(char *s) /* get body of X ... X */ + /* message if too big */ +{ + int c, delim, rdelim, n, deep; + static char *buf = NULL; + static int nbuf = 0; + char *p; + + if (buf == NULL) + buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0])); + while ((delim = input()) == ' ' || delim == '\t' || delim == '\n') + ; + rdelim = baldelim(delim, "{}"); /* could be "(){}[]`'" */ + deep = 1; + for (p = buf; ; ) { + c = input(); + if (c == rdelim) + if (--deep == 0) + break; + if (c == delim) + deep++; + if (p >= buf + nbuf) { + n = p - buf; + buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0])); + p = buf + n; + } + if (c == EOF) + ERROR "end of file in %s %c %.20s... %c", s, delim, buf, delim FATAL; + *p++ = c; + } + *p = '\0'; + dprintf("delimstr %s %c <%s> %c\n", s, delim, buf, delim); + return tostring(buf); +} + +baldelim(int c, char *s) /* replace c by balancing entry in s */ +{ + for ( ; *s; s += 2) + if (*s == c) + return s[1]; + return c; +} + +Arg args[10]; /* argument frames */ +Arg *argfp = args; /* frame pointer */ +int argcnt; /* number of arguments seen so far */ + +void dodef(Obj *stp) /* collect args and switch input to defn */ +{ + int i, len; + char *p; + Arg *ap; + + ap = argfp+1; + if (ap >= args+10) + ERROR "arguments too deep" FATAL; + argcnt = 0; + if (input() != '(') + ERROR "disaster in dodef" FATAL; + if (ap->argval == 0) + ap->argval = malloc(1000); + for (p = ap->argval; (len = getarg(p)) != -1; p += len) { + ap->argstk[argcnt++] = p; + if (input() == ')') + break; + } + for (i = argcnt; i < MAXARGS; i++) + ap->argstk[i] = ""; + if (dbg) + for (i = 0; i < argcnt; i++) + printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]); + argfp = ap; + pushsrc(Macro, stp->val); +} + +getarg(char *p) /* pick up single argument, store in p, return length */ +{ + int n, c, npar; + + n = npar = 0; + for ( ;; ) { + c = input(); + if (c == EOF) + ERROR "end of file in getarg!" FATAL; + if (npar == 0 && (c == ',' || c == ')')) + break; + if (c == '"') /* copy quoted stuff intact */ + do { + *p++ = c; + n++; + } while ((c = input()) != '"' && c != EOF); + else if (c == '(') + npar++; + else if (c == ')') + npar--; + n++; + *p++ = c; + } + *p = 0; + unput(c); + return(n + 1); +} + +#define PBSIZE 2000 +char pbuf[PBSIZE]; /* pushback buffer */ +char *pb = pbuf-1; /* next pushed back character */ + +char ebuf[200]; /* collect input here for error reporting */ +char *ep = ebuf; + +int begin = 0; +extern int thru; +extern Obj *thrudef; +extern char *untilstr; + +input(void) +{ + register int c; + + if (thru && begin) { + do_thru(); + begin = 0; + } + c = nextchar(); + dprintf(" <%c>", c); + if (ep >= ebuf + sizeof ebuf) + ep = ebuf; + return *ep++ = c; +} + +nextchar(void) +{ + register int c; + + loop: + switch (srcp->type) { + case Free: /* free string */ + free(srcp->sp); + popsrc(); + goto loop; + case Thru: /* end of pushed back line */ + begin = 1; + popsrc(); + c = '\n'; + break; + case Char: + if (pb >= pbuf) { + c = *pb--; + popsrc(); + break; + } else { /* can't happen? */ + popsrc(); + goto loop; + } + case String: + c = *srcp->sp++; + if (c == '\0') { + popsrc(); + goto loop; + } else { + if (*srcp->sp == '\0') /* empty, so pop */ + popsrc(); + break; + } + case Macro: + c = *srcp->sp++; + if (c == '\0') { + if (--argfp < args) + ERROR "argfp underflow" FATAL; + popsrc(); + goto loop; + } else if (c == '$' && isdigit(*srcp->sp)) { /* $3 */ + int n = 0; + while (isdigit(*srcp->sp)) + n = 10 * n + *srcp->sp++ - '0'; + if (n > 0 && n <= MAXARGS) + pushsrc(String, argfp->argstk[n-1]); + goto loop; + } + break; + case File: + c = getc(curfile->fin); + if (c == EOF) { + if (curfile == infile) + ERROR "end of file inside .G1/.G2" FATAL; + if (curfile->fin != stdin) { + fclose(curfile->fin); + free(curfile->fname); /* assumes allocated */ + } + curfile--; + printf(".lf %d %s\n", curfile->lineno, curfile->fname); + popsrc(); + thru = 0; /* chicken out */ + thrudef = 0; + if (untilstr) { + free(untilstr); + untilstr = 0; + } + goto loop; + } + if (c == '\n') + curfile->lineno++; + break; + } + return c; +} + +void do_thru(void) /* read one line, make into a macro expansion */ +{ + int c, i; + char *p; + Arg *ap; + + ap = argfp+1; + if (ap >= args+10) + ERROR "arguments too deep" FATAL; + if (ap->argval == NULL) + ap->argval = malloc(1000); + p = ap->argval; + argcnt = 0; + c = nextchar(); + if (thru == 0) { /* end of file was seen, so thru is done */ + unput(c); + return; + } + for ( ; c != '\n' && c != EOF; ) { + if (c == ' ' || c == '\t') { + c = nextchar(); + continue; + } + if (argcnt >= MAXARGS) + ERROR "too many fields on input line" FATAL; + ap->argstk[argcnt++] = p; + if (c == '"') { + do { + *p++ = c; + if ((c = nextchar()) == '\\') { + *p++ = c; + *p++ = nextchar(); + c = nextchar(); + } + } while (c != '"' && c != '\n' && c != EOF); + *p++ = '"'; + if (c == '"') + c = nextchar(); + } else { + do { + *p++ = c; + } while ((c = nextchar())!=' ' && c!='\t' && c!='\n' && c!=',' && c!=EOF); + if (c == ',') + c = nextchar(); + } + *p++ = '\0'; + } + if (c == EOF) + ERROR "unexpected end of file in do_thru" FATAL; + if (argcnt == 0) { /* ignore blank line */ + pushsrc(Thru, (char *) 0); + return; + } + for (i = argcnt; i < MAXARGS; i++) + ap->argstk[i] = ""; + if (dbg) + for (i = 0; i < argcnt; i++) + printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]); + if (strcmp(ap->argstk[0], ".G2") == 0) { + thru = 0; + thrudef = 0; + pushsrc(String, "\n.G2\n"); + return; + } + if (untilstr && strcmp(ap->argstk[0], untilstr) == 0) { + thru = 0; + thrudef = 0; + free(untilstr); + untilstr = 0; + return; + } + pushsrc(Thru, (char *) 0); + dprintf("do_thru pushing back <%s>\n", thrudef->val); + argfp = ap; + pushsrc(Macro, thrudef->val); +} + +unput(int c) +{ + if (++pb >= pbuf + sizeof pbuf) + ERROR "pushback overflow" FATAL; + if (--ep < ebuf) + ep = ebuf + sizeof(ebuf) - 1; + *pb = c; + pushsrc(Char, pb); + return c; +} + +void pbstr(char *s) +{ + pushsrc(String, s); +} + +double errcheck(double x, char *s) +{ + extern int errno; + + if (errno == EDOM) { + errno = 0; + ERROR "%s argument out of domain", s WARNING; + } else if (errno == ERANGE) { + errno = 0; + ERROR "%s result out of range", s WARNING; + } + return x; +} + +char errbuf[200]; + +void yyerror(char *s) +{ + extern char *cmdname; + int ern = errno; /* cause some libraries clobber it */ + + if (synerr) + return; + fflush(stdout); + fprintf(stderr, "%s: %s", cmdname, s); + if (ern > 0) { + errno = ern; + perror("???"); + } + fprintf(stderr, " near %s:%d\n", + curfile->fname, curfile->lineno+1); + eprint(); + synerr = 1; + errno = 0; +} + +void eprint(void) /* try to print context around error */ +{ + char *p, *q; + + p = ep - 1; + if (p > ebuf && *p == '\n') + p--; + for ( ; p >= ebuf && *p != '\n'; p--) + ; + while (*p == '\n') + p++; + fprintf(stderr, " context is\n\t"); + for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) + ; + for (; p < q; p++) + if (isprint(*p)) + putc(*p, stderr); + fprintf(stderr, " >>> "); + for (; p < q; p++) + if (isprint(*p)) + putc(*p, stderr); + fprintf(stderr, " <<< "); + while (pb >= pbuf) + putc(*pb--, stderr); + fgets(ebuf, sizeof ebuf, curfile->fin); + fprintf(stderr, "%s", ebuf); + pbstr("\n.G2\n"); /* safety first */ + ep = ebuf; +} + +int yywrap(void) {return 1;} + +char *newfile = 0; /* filename for file copy */ +char *untilstr = 0; /* string that terminates a thru */ +int thru = 0; /* 1 if copying thru macro */ +Obj *thrudef = 0; /* macro being used */ + +void copyfile(char *s) /* remember file to start reading from */ +{ + newfile = s; +} + +void copydef(Obj *p) /* remember macro Obj */ +{ + thrudef = p; +} + +Obj *copythru(char *s) /* collect the macro name or body for thru */ +{ + Obj *p; + char *q; + + p = lookup(s, 0); + if (p != NULL) { + if (p->type == DEFNAME) { + p->val = addnewline(p->val); + return p; + } else + ERROR "%s used as define and name", s FATAL; + } + /* have to collect the definition */ + pbstr(s); /* first char is the delimiter */ + q = delimstr("thru body"); + p = lookup("nameless", 1); + if (p != NULL) + if (p->val) + free(p->val); + p->type = DEFNAME; + p->val = q; + p->val = addnewline(p->val); + dprintf("installing nameless as `%s'\n", p->val); + return p; +} + +char *addnewline(char *p) /* add newline to end of p */ +{ + int n; + + n = strlen(p); + if (p[n-1] != '\n') { + p = realloc(p, n+2); + p[n] = '\n'; + p[n+1] = '\0'; + } + return p; +} + +void copyuntil(char *s) /* string that terminates a thru */ +{ + untilstr = s; +} + +void copy(void) /* begin input from file, etc. */ +{ + FILE *fin; + + if (newfile) { + if ((fin = fopen(unsharp(newfile), "r")) == NULL) + ERROR "can't open file %s", newfile FATAL; + curfile++; + curfile->fin = fin; + curfile->fname = tostring(newfile); + curfile->lineno = 0; + printf(".lf 1 %s\n", curfile->fname); + pushsrc(File, curfile->fname); + newfile = 0; + } + if (thrudef) { + thru = 1; + begin = 1; /* wrong place */ + } +} + +char shellbuf[1000], *shellp; + +void shell_init(void) /* set up to interpret a shell command */ +{ + fprintf(tfd, "# shell cmd...\n"); + sprintf(shellbuf, "rc -c '"); + shellp = shellbuf + strlen(shellbuf); +} + +void shell_text(char *s) /* add string to command being collected */ +{ + /* fprintf(tfd, "#add <%s> to <%s>\n", s, shellbuf); */ + while (*s) { + if (*s == '\'') { /* protect interior quotes */ + *shellp++ = '\''; + *shellp++ = '\\'; + *shellp++ = '\''; + } + *shellp++ = *s++; + } +} + +void shell_exec(void) /* do it */ +{ + /* fprintf(tfd, "# run <%s>\n", shellbuf); */ + *shellp++ = '\''; + *shellp = '\0'; + system(shellbuf); +} diff --git a/src/cmd/grap/label.c b/src/cmd/grap/label.c new file mode 100644 index 00000000..e788dd9d --- /dev/null +++ b/src/cmd/grap/label.c @@ -0,0 +1,123 @@ +#include +#include +#include "grap.h" +#include "y.tab.h" + +int pointsize = 10; /* assumed pointsize to start */ +int ps_set = 0; /* someone has set pointsize explicitly */ + +double textht = 1.0/6.0; /* 6 lines/inch */ +double textwid = 1; /* width of text box for vertical */ + +double lab_up = 0.0; /* extra motion for label */ +double lab_rt = 0.0; /* extra motion for label */ +double lab_wid = 0.0; /* override default width computation */ + +void labelwid(double amt) +{ + lab_wid = amt + .00001; +} + +void labelmove(int dir, double amt) /* record direction & motion of position corr */ +{ + switch (dir) { + case UP: lab_up += amt; break; + case DOWN: lab_up -= amt; break; + case LEFT: lab_rt -= amt; break; + case RIGHT: lab_rt += amt; break; + } +} + +void label(int label_side, Attr *stringlist) /* stick label on label_side */ +{ + int m; + Attr *ap; + + fprintf(tfd, "\ttextht = %g\n", textht); + if (lab_wid != 0.0) { + fprintf(tfd, "\ttextwid = %g\n", lab_wid); + lab_wid = 0; + } else if (label_side == LEFT || label_side == RIGHT) { + textwid = 0; + for (ap = stringlist; ap != NULL; ap = ap->next) + if ((m = strlen(ap->sval)) > textwid) + textwid = m; + textwid /= 15; /* estimate width at 15 chars/inch */ + fprintf(tfd, "\ttextwid = %g\n", textwid); + } + fprintf(tfd, "Label:\t%s", slprint(stringlist)); + freeattr(stringlist); + switch (label_side) { + case BOT: + case 0: + fprintf(tfd, " with .n at Frame.s - (0,2 * textht)"); + break; + case LEFT: + fprintf(tfd, " wid textwid with .e at Frame.w - (0.2,0)"); + break; + case RIGHT: + fprintf(tfd, " wid textwid with .w at Frame.e + (0.2,0)"); + break; + case TOP: + fprintf(tfd, " with .s at Frame.n + (0,2 * textht)"); + break; + } + lab_adjust(); + fprintf(tfd, "\n"); + label_side = BOT; +} + +void lab_adjust(void) /* add a string to adjust labels, ticks, etc. */ +{ + if (lab_up != 0.0 || lab_rt != 0.0) + fprintf(tfd, " + (%g,%g)", lab_rt, lab_up); +} + +char *sizeit(Attr *ap) /* add \s..\s to ap->sval */ +{ + int n; + static char buf[1000]; + + if (!ap->op) { /* no explicit size command */ + if (ps_set) { + sprintf(buf, "\\s%d%s\\s0", pointsize, ap->sval); + return buf; + } else + return ap->sval; + } else if (!ps_set) { /* explicit size but no global size */ + n = (int) ap->fval; + switch (ap->op) { + case ' ': /* absolute size */ + sprintf(buf, "\\s%d%s\\s0", n, ap->sval); + break; + case '+': /* better be only one digit! */ + sprintf(buf, "\\s+%d%s\\s-%d", n, ap->sval, n); + break; + case '-': + sprintf(buf, "\\s-%d%s\\s+%d", n, ap->sval, n); + break; + case '*': + case '/': + return ap->sval; /* ignore for now */ + } + return buf; + } else { + /* explicit size and a global background size */ + n = (int) ap->fval; + switch (ap->op) { + case ' ': /* absolute size */ + sprintf(buf, "\\s%d%s\\s0", n, ap->sval); + break; + case '+': + sprintf(buf, "\\s%d%s\\s0", pointsize+n, ap->sval); + break; + case '-': + sprintf(buf, "\\s%d%s\\s0", pointsize-n, ap->sval); + break; + case '*': + case '/': + return ap->sval; /* ignore for now */ + } + return buf; + } +} diff --git a/src/cmd/grap/main.c b/src/cmd/grap/main.c new file mode 100644 index 00000000..0aa67417 --- /dev/null +++ b/src/cmd/grap/main.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +int dbg = 0; + +#ifndef GRAPDEFINES +#define GRAPDEFINES "#9/sys/lib/grap.defines" +#endif +char *lib_defines = GRAPDEFINES; + +int lib = 1; /* 1 to include lib_defines */ +FILE *tfd = NULL; +char tempfile[L_tmpnam]; + +int synerr = 0; +int codegen = 0; /* 1=>output for this picture; 0=>no output */ +char *cmdname; + +Obj *objlist = NULL; /* all names stored here */ + +#define BIG 1e30 +Point ptmin = { NULL, -BIG, -BIG }; +Point ptmax = { NULL, BIG, BIG }; + +char *version = "version Dec 30, 1995"; + +extern int yyparse(void); +extern void setdefaults(void); +extern void getdata(void); +extern int unlink(char *); + +main(int argc, char *argv[]) +{ + extern void onintr(int), fpecatch(int); + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); + signal(SIGFPE, fpecatch); + cmdname = argv[0]; + tmpnam(tempfile); + while (argc > 1 && *argv[1] == '-') { + switch (argv[1][1]) { + case 'd': + dbg = 1; + tfd = stdout; + strcpy(tempfile, "grap.temp"); + unlink(tempfile); + fprintf(stderr, "%s\n", version); + break; + case 'l': /* turn off /usr/lib inclusion */ + lib = 0; + break; + } + argc--; + argv++; + } + setdefaults(); + curfile = infile; + if (argc <= 1) { + curfile->fin = stdin; + curfile->fname = tostring("-"); + pushsrc(File, curfile->fname); + getdata(); + } else + while (argc-- > 1) { + if ((curfile->fin = fopen(*++argv, "r")) == NULL) { + fprintf(stderr, "grap: can't open %s\n", *argv); + onintr(0); + } + curfile->fname = tostring(*argv); + pushsrc(File, curfile->fname); + getdata(); + fclose(curfile->fin); + free(curfile->fname); + } + if (!dbg) + unlink(tempfile); + exit(0); +} + +void onintr(int n) +{ + n; + if (!dbg) + unlink(tempfile); + exit(1); +} + +void fpecatch(int n) +{ + ERROR "floating point exception" WARNING; + onintr(n); +} + +char *grow(char *ptr, char *name, int num, int size) /* make array bigger */ +{ + char *p; + + if (ptr == NULL) + p = malloc(num * size); + else + p = realloc(ptr, num * size); + if (p == NULL) + ERROR "can't grow %s to %d", name, num * size FATAL; + return p; +} + +static struct { + char *name; + double val; +} defaults[] ={ + "frameht", FRAMEHT, + "framewid", FRAMEWID, + "ticklen", TICKLEN, + "slop", SLOP, + NULL, 0 +}; + +void setdefaults(void) /* set default sizes for variables */ +{ + int i; + Obj *p; + + for (i = 0; defaults[i].name != NULL; i++) { + p = lookup(defaults[i].name, 1); + setvar(p, defaults[i].val); + } +} + +void getdata(void) /* read input */ +{ + register FILE *fin; + char buf[1000], buf1[100]; + int ln; + + fin = curfile->fin; + curfile->lineno = 0; + printf(".lf 1 %s\n", curfile->fname); + while (fgets(buf, sizeof buf, fin) != NULL) { + curfile->lineno++; + if (*buf == '.' && *(buf+1) == 'G' && *(buf+2) == '1') { + setup(); + fprintf(stdout, ".PS%s", &buf[3]); /* maps .G1 [w] to .PS w */ + printf("scale = 1\n"); /* defends against cip users */ + printf(".lf %d\n", curfile->lineno+1); + yyparse(); + fprintf(stdout, ".PE\n"); + printf(".lf %d\n", curfile->lineno+1); + fflush(stdout); + } else if (buf[0] == '.' && buf[1] == 'l' && buf[2] == 'f') { + if (sscanf(buf+3, "%d %s", &ln, buf1) == 2) { + free(curfile->fname); + printf(".lf %d %s\n", curfile->lineno = ln, curfile->fname = tostring(buf1)); + } else + printf(".lf %d\n", curfile->lineno = ln); + } else + fputs(buf, stdout); + } +} diff --git a/src/cmd/grap/misc.c b/src/cmd/grap/misc.c new file mode 100644 index 00000000..7a68a692 --- /dev/null +++ b/src/cmd/grap/misc.c @@ -0,0 +1,263 @@ +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +int nnum = 0; /* number of saved numbers */ +double num[MAXNUM]; + +int just; /* current justification mode (RJUST, etc.) */ +int sizeop; /* current optional operator for size change */ +double sizexpr; /* current size change expression */ + +void savenum(int n, double f) /* save f in num[n] */ +{ + num[n] = f; + nnum = n+1; + if (nnum >= MAXNUM) + ERROR "too many numbers" WARNING; +} + +void setjust(int j) +{ + just |= j; +} + +void setsize(int op, double expr) +{ + sizeop = op; + sizexpr = expr; +} + +char *tostring(char *s) +{ + register char *p; + + p = malloc(strlen(s)+1); + if (p == NULL) + ERROR "out of space in tostring on %s", s FATAL; + strcpy(p, s); + return(p); +} + +void range(Point pt) /* update the range for point pt */ +{ + Obj *p = pt.obj; + + if (!(p->coord & XFLAG)) { + if (pt.x > p->pt1.x) + p->pt1.x = pt.x; + if (pt.x < p->pt.x) + p->pt.x = pt.x; + } + if (!(p->coord & YFLAG)) { + if (pt.y > p->pt1.y) + p->pt1.y = pt.y; + if (pt.y < p->pt.y) + p->pt.y = pt.y; + } +} + +void halfrange(Obj *p, int side, double val) /* record max and min for one direction */ +{ + if (!(p->coord&XFLAG) && (side == LEFT || side == RIGHT)) { + if (val < p->pt.y) + p->pt.y = val; + if (val > p->pt1.y) + p->pt1.y = val; + } else if (!(p->coord&YFLAG) && (side == TOP || side == BOT)) { + if (val < p->pt.x) + p->pt.x = val; + if (val > p->pt1.x) + p->pt1.x = val; + } +} + + +Obj *lookup(char *s, int inst) /* find s in objlist, install if inst */ +{ + Obj *p; + int found = 0; + + for (p = objlist; p; p = p->next){ + if (strcmp(s, p->name) == 0) { + found = 1; + break; + } + } + if (p == NULL && inst != 0) { + p = (Obj *) calloc(1, sizeof(Obj)); + if (p == NULL) + ERROR "out of space in lookup" FATAL; + p->name = tostring(s); + p->type = NAME; + p->pt = ptmax; + p->pt1 = ptmin; + p->fval = 0.0; + p->next = objlist; + objlist = p; + } + dprintf("lookup(%s,%d) = %d\n", s, inst, found); + return p; +} + +double getvar(Obj *p) /* return value of variable */ +{ + return p->fval; +} + +double setvar(Obj *p, double f) /* set value of variable to f */ +{ + if (strcmp(p->name, "pointsize") == 0) { /* kludge */ + pointsize = f; + ps_set = 1; + } + p->type = VARNAME; + return p->fval = f; +} + +Point makepoint(Obj *s, double x, double y) /* make a Point */ +{ + Point p; + + dprintf("makepoint: %s, %g,%g\n", s->name, x, y); + p.obj = s; + p.x = x; + p.y = y; + return p; +} + +Attr *makefattr(int type, double fval) /* set double in attribute */ +{ + return makeattr(type, fval, (char *) 0, 0, 0); +} + +Attr *makesattr(char *s) /* make an Attr cell containing s */ +{ + Attr *ap = makeattr(STRING, sizexpr, s, just, sizeop); + just = sizeop = 0; + sizexpr = 0.0; + return ap; +} + +Attr *makeattr(int type, double fval, char *sval, int just, int op) +{ + Attr *a; + + a = (Attr *) malloc(sizeof(Attr)); + if (a == NULL) + ERROR "out of space in makeattr" FATAL; + a->type = type; + a->fval = fval; + a->sval = sval; + a->just = just; + a->op = op; + a->next = NULL; + return a; +} + +Attr *addattr(Attr *a1, Attr *ap) /* add attr ap to end of list a1 */ +{ + Attr *p; + + if (a1 == 0) + return ap; + if (ap == 0) + return a1; + for (p = a1; p->next; p = p->next) + ; + p->next = ap; + return a1; +} + +void freeattr(Attr *ap) /* free an attribute list */ +{ + Attr *p; + + while (ap) { + p = ap->next; /* save next */ + if (ap->sval) + free(ap->sval); + free((char *) ap); + ap = p; + } +} + +char *slprint(Attr *stringlist) /* print strings from stringlist */ +{ + int ntext, n, last_op, last_just; + double last_fval; + static char buf[1000]; + Attr *ap; + + buf[0] = '\0'; + last_op = last_just = 0; + last_fval = 0.0; + for (ntext = 0, ap = stringlist; ap != NULL; ap = ap->next) + ntext++; + sprintf(buf, "box invis wid 0 ht %d*textht", ntext); + n = strlen(buf); + for (ap = stringlist; ap != NULL; ap = ap->next) { + if (ap->op == 0) { /* propagate last value */ + ap->op = last_op; + ap->fval = last_fval; + } else { + last_op = ap->op; + last_fval = ap->fval; + } + sprintf(buf+n, " \"%s\"", ps_set || ap->op ? sizeit(ap) : ap->sval); + if (ap->just) + last_just = ap->just; + if (last_just) + strcat(buf+n, juststr(last_just)); + n = strlen(buf); + } + return buf; /* watch it: static */ +} + +char *juststr(int j) /* convert RJUST, etc., into string */ +{ + static char buf[50]; + + buf[0] = '\0'; + if (j & RJUST) + strcat(buf, " rjust"); + if (j & LJUST) + strcat(buf, " ljust"); + if (j & ABOVE) + strcat(buf, " above"); + if (j & BELOW) + strcat(buf, " below"); + return buf; /* watch it: static */ +} + +char *sprntf(char *s, Attr *ap) /* sprintf(s, attrlist ap) */ +{ + char buf[500]; + int n; + Attr *p; + + for (n = 0, p = ap; p; p = p->next) + n++; + switch (n) { + case 0: + return s; + case 1: + sprintf(buf, s, ap->fval); + break; + case 2: + sprintf(buf, s, ap->fval, ap->next->fval); + break; + case 3: + sprintf(buf, s, ap->fval, ap->next->fval, ap->next->next->fval); + break; + case 5: + ERROR "too many expressions in sprintf" WARNING; + case 4: + sprintf(buf, s, ap->fval, ap->next->fval, ap->next->next->fval, ap->next->next->next->fval); + break; + } + free(s); + return tostring(buf); +} diff --git a/src/cmd/grap/mkfile b/src/cmd/grap/mkfile new file mode 100644 index 00000000..872497d1 --- /dev/null +++ b/src/cmd/grap/mkfile @@ -0,0 +1,37 @@ +<$PLAN9/src/mkhdr + +TARG=grap +OFILES=\ + grap.$O\ + grapl.$O\ + main.$O\ + input.$O\ + print.$O\ + frame.$O\ + for.$O\ + coord.$O\ + ticks.$O\ + plot.$O\ + label.$O\ + misc.$O\ + +HFILES=grap.h\ + y.tab.h\ + +YFILES=grap.y\ + +LFILES=grapl.lx\ + +SHORTLIB=bio 9 +<$PLAN9/src/mkone +YFLAGS = -d -S +LEX=9lex + +grap.c: y.tab.c + mv $prereq $target + +grapl.c: $LFILES + $LEX -t $prereq > $target + +clean:V: + rm -f [$OS].out *.[$OS] y.tab.? lex.yy.c grapl.c grap.c grap diff --git a/src/cmd/grap/plot.c b/src/cmd/grap/plot.c new file mode 100644 index 00000000..ab375953 --- /dev/null +++ b/src/cmd/grap/plot.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +void line(int type, Point p1, Point p2, Attr *desc) /* draw a line segment */ +{ + fprintf(tfd, "%s %s from %s", + type==LINE ? "line" : "arrow", desc_str(desc), xyname(p1)); + fprintf(tfd, " to %s", xyname(p2)); /* 'cause xyname is botched */ + fprintf(tfd, "\n"); + range(p1); + range(p2); +} + +void circle(double r, Point pt) /* draw a circle */ +{ + if (r > 0.0) + fprintf(tfd, "circle rad %g at %s\n", r, xyname(pt)); + else + fprintf(tfd, "\"\\s-3\\(ob\\s0\" at %s\n", xyname(pt)); + range(pt); +} + +char *xyname(Point pt) /* generate xy name macro for point p */ +{ + static char buf[200]; + Obj *p; + + p = pt.obj; + if (p->log & XFLAG) { + if (pt.x <= 0.0) + ERROR "can't take log of x coord %g", pt.x FATAL; + logit(pt.x); + } + if (p->log & YFLAG) { + if (pt.y <= 0.0) + ERROR "can't take log of y coord %g", pt.y FATAL; + logit(pt.y); + } + sprintf(buf, "xy_%s(%g,%g)", p->name, pt.x, pt.y); + return buf; /* WATCH IT: static */ +} + +void pic(char *s) /* fire out pic stuff directly */ +{ + while (*s == ' ') + s++; + fprintf(tfd, "%s\n", s); +} + +int auto_x = 0; /* counts abscissa if none provided */ + +void numlist(void) /* print numbers in default way */ +{ + Obj *p; + Point pt; + int i; + static char *spot = "\\(bu"; + Attr *ap; + + p = pt.obj = lookup(curr_coord, 1); + if (nnum == 1) { + nnum = 2; + num[1] = num[0]; + num[0] = ++auto_x; + } + pt.x = num[0]; + if (p->attr && p->attr->sval) + spot = p->attr->sval; + for (i = 1; i < nnum; i++) { + pt.y = num[i]; + if (p->attr == 0 || p->attr->type == 0) { + ap = makesattr(tostring(spot)); + plot(ap, pt); + } else + next(p, pt, p->attr); + } + nnum = 0; +} + +void plot(Attr *sl, Point pt) /* put stringlist sl at point pt */ +{ + fprintf(tfd, "%s at %s\n", slprint(sl), xyname(pt)); + range(pt); + freeattr(sl); +} + +void plotnum(double f, char *fmt, Point pt) /* plot value f at point */ +{ + char buf[100]; + + if (fmt) { + sprintf(buf, fmt, f); + free(fmt); + } else if (f >= 0.0) + sprintf(buf, "%g", f); + else + sprintf(buf, "\\-%g", -f); + fprintf(tfd, "\"%s\" at %s\n", buf, xyname(pt)); + range(pt); +} + +void drawdesc(int type, Obj *p, Attr *desc, char *s) /* set line description for p */ +{ + p->attr = desc; + p->attr->sval = s; + if (type == NEW) { + p->first = 0; /* so it really looks new */ + auto_x = 0; + } +} + +void next(Obj *p, Point pt, Attr *desc) /* add component to a path */ +{ + char *s; + + if (p->first == 0) { + p->first++; + fprintf(tfd, "L%s: %s\n", p->name, xyname(pt)); + } else { + fprintf(tfd, "line %s from L%s to %s; L%s: Here\n", + desc_str(desc->type ? desc : p->attr), + p->name, xyname(pt), p->name); + } + if (p->attr && (s=p->attr->sval)) { + /* BUG: should fix size here */ + fprintf(tfd, "\"%s\" at %s\n", s, xyname(pt)); + } + range(pt); +} diff --git a/src/cmd/grap/print.c b/src/cmd/grap/print.c new file mode 100644 index 00000000..8f553c8f --- /dev/null +++ b/src/cmd/grap/print.c @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +double margin = MARGIN; /* extra space around edges */ +extern double frame_ht, frame_wid, ticklen; +extern int just, sizeop, tick_dir; +extern double sizexpr, lab_up, lab_rt; + +char graphname[50] = "Graph"; +char graphpos[200] = ""; + +void print(void) /* arrange final output */ +{ + FILE *fd; + Obj *p, *dfp; + int c; + double dx, dy, xfac, yfac; + + if (tfd != NULL) { + fclose(tfd); /* end the temp file */ + tfd = stdout; + } + + if ((p=lookup("margin",0)) != NULL) + margin = p->fval; + if (frame_ht < 0) /* wasn't set explicitly, so use default */ + frame_ht = getvar(lookup("frameht", 0)); + if (frame_wid < 0) + frame_wid = getvar(lookup("framewid", 0)); + dfp = NULL; + for (p = objlist; p; p = p->next) { + dprintf("print: name = <%s>, type = %d\n", p->name, p->type); + if (p->type == NAME) { + Point pt, pt1; + pt = p->pt; + pt1 = p->pt1; + fprintf(tfd, "\t# %s %g .. %g, %g .. %g\n", + p->name, pt.x, pt1.x, pt.y, pt1.y); + if (p->log & XFLAG) { + if (pt.x <= 0.0) + ERROR "can't take log of x coord %g", pt.x FATAL; + logit(pt.x); + logit(pt1.x); + } + if (p->log & YFLAG) { + if (pt.y <= 0.0) + ERROR "can't take log of y coord %g", pt.y FATAL; + logit(pt.y); + logit(pt1.y); + } + if (!(p->coord & XFLAG)) { + dx = pt1.x - pt.x; + pt.x -= margin * dx; + pt1.x += margin * dx; + } + if (!(p->coord & YFLAG)) { + dy = pt1.y - pt.y; + pt.y -= margin * dy; + pt1.y += margin * dy; + } + if (autoticks && strcmp(p->name, dflt_coord) == 0) { + p->pt = pt; + p->pt1 = pt1; + if (p->log & XFLAG) { + p->pt.x = pow(10.0, pt.x); + p->pt1.x = pow(10.0, pt1.x); + } + if (p->log & YFLAG) { + p->pt.y = pow(10.0, pt.y); + p->pt1.y = pow(10.0, pt1.y); + } + dfp = setauto(); + } + dx = pt1.x - pt.x; + dy = pt1.y - pt.y; + xfac = dx > 0 ? frame_wid/dx : frame_wid/2; + yfac = dy > 0 ? frame_ht/dy : frame_ht/2; + + fprintf(tfd, "define xy_%s @ ", p->name); + if (dx > 0) + fprintf(tfd, "\t(($1)-(%g))*%g", pt.x, xfac); + else + fprintf(tfd, "\t%g", xfac); + if (dy > 0) + fprintf(tfd, ", (($2)-(%g))*%g @\n", pt.y, yfac); + else + fprintf(tfd, ", %g @\n", yfac); + fprintf(tfd, "define x_%s @ ", p->name); + if (dx > 0) + fprintf(tfd, "\t(($1)-(%g))*%g @\n", pt.x, xfac); + else + fprintf(tfd, "\t%g @\n", xfac); + fprintf(tfd, "define y_%s @ ", p->name); + if (dy > 0) + fprintf(tfd, "\t(($1)-(%g))*%g @\n", pt.y, yfac); + else + fprintf(tfd, "\t%g @\n", yfac); + } + } + if (codegen) + frame(); + if (codegen && autoticks && dfp) + do_autoticks(dfp); + + if ((fd = fopen(tempfile, "r")) != NULL) { + while ((c = getc(fd)) != EOF) + putc(c, tfd); + fclose(fd); + } + tfd = NULL; +} + +void endstat(void) /* clean up after each statement */ +{ + + just = sizeop = 0; + lab_up = lab_rt = 0.0; + sizexpr = 0.0; + nnum = 0; + ntick = 0; + tside = 0; + tick_dir = OUT; + ticklen = TICKLEN; +} + +void graph(char *s) /* graph statement */ +{ + char *p, *os; + int c; + + if (codegen) { + fprintf(stdout, "%s: [\n", graphname); + print(); /* pump out previous graph */ + fprintf(stdout, "\n] %s\n", graphpos); + reset(); + } + if (s) { + dprintf("into graph with <%s>\n", s); + opentemp(); + os = s; + while ((c = *s) == ' ' || c == '\t') + s++; + if (c == '\0') + ERROR "no name on graph statement" WARNING; + if (!isupper(s[0])) + ERROR "graph name %s must be capitalized", s WARNING; + for (p=graphname; (c = *s) != ' ' && c != '\t' && c != '\0'; ) + *p++ = *s++; + *p = '\0'; + strcpy(graphpos, s); + dprintf("graphname = <%s>, graphpos = <%s>\n", graphname, graphpos); + free(os); + } +} + +void setup(void) /* done at each .G1 */ +{ + static int firstG1 = 0; + + reset(); + opentemp(); + frame_ht = frame_wid = -1; /* reset in frame() */ + ticklen = getvar(lookup("ticklen", 0)); + if (firstG1++ == 0) + do_first(); + codegen = synerr = 0; + strcpy(graphname, "Graph"); + strcpy(graphpos, ""); +} + +void do_first(void) /* done at first .G1: definitions, etc. */ +{ + extern int lib; + extern char *lib_defines; + static char buf[50], buf1[50]; /* static because pbstr uses them */ + FILE *fp; + extern int getpid(void); + + sprintf(buf, "define pid /%d/\n", getpid()); + pbstr(buf); + if (lib != 0) { + if ((fp = fopen(unsharp(lib_defines), "r")) != NULL) { + sprintf(buf1, "copy \"%s\"\n", lib_defines); + pbstr(buf1); + fclose(fp); + } else { + fprintf(stderr, "grap warning: can't open %s\n", lib_defines); + } + } +} + +void reset(void) /* done at each "graph ..." statement */ +{ + Obj *p, *np, *deflist; + extern int tlist, toffside, autodir; + + curr_coord = dflt_coord; + ncoord = auto_x = 0; + autoticks = LEFT|BOT; + autodir = 0; + tside = tlist = toffside = 0; + tick_dir = OUT; + margin = MARGIN; + deflist = NULL; + for (p = objlist; p; p = np) { + np = p->next; + if (p->type == DEFNAME || p->type == VARNAME) { + p->next = deflist; + deflist = p; + } else { + free(p->name); + freeattr(p->attr); + free((char *) p); + } + } + objlist = deflist; +} + +void opentemp(void) +{ + if (tfd != NULL) + fclose(tfd); + if (tfd != stdout) { +// if (tfd != NULL) +// fclose(tfd); + if ((tfd = fopen(tempfile, "w")) == NULL) { + fprintf(stderr, "grap: can't open %s\n", tempfile); + exit(1); + } + } +} diff --git a/src/cmd/grap/ticks.c b/src/cmd/grap/ticks.c new file mode 100644 index 00000000..72fc06f5 --- /dev/null +++ b/src/cmd/grap/ticks.c @@ -0,0 +1,492 @@ +#include +#include +#include +#include +#include "grap.h" +#include "y.tab.h" + +#define MAXTICK 200 +int ntick = 0; +double tickval[MAXTICK]; /* tick values (one axis at a time */ +char *tickstr[MAXTICK]; /* and labels */ + +int tside = 0; +int tlist = 0; /* 1 => explicit values given */ +int toffside = 0; /* no ticks on these sides */ +int goffside = 0; /* no ticks on grid on these sides */ +int tick_dir = OUT; +double ticklen = TICKLEN; /* default tick length */ +int autoticks = LEFT|BOT; +int autodir = 0; /* set LEFT, etc. if automatic ticks go in */ + +void savetick(double f, char *s) /* remember tick location and label */ +{ + if (ntick >= MAXTICK) + ERROR "too many ticks (%d)", MAXTICK FATAL; + tickval[ntick] = f; + tickstr[ntick] = s; + ntick++; +} + +void dflt_tick(double f) +{ + if (f >= 0.0) + savetick(f, tostring("%g")); + else + savetick(f, tostring("\\%g")); +} + +void tickside(int n) /* remember which side these ticks/gridlines go on */ +{ + tside |= n; +} + +void tickoff(int side) /* remember explicit sides */ +{ + toffside |= side; +} + +void gridtickoff(void) /* turn grid ticks off on the side previously specified (ugh) */ +{ + goffside = tside; +} + +void setlist(void) /* remember that there was an explicit list */ +{ + tlist = 1; +} + +void tickdir(int dir, double val, int explicit) /* remember in/out [expr] */ +{ + tick_dir = dir; + if (explicit) + ticklen = val; +} + +void ticks(void) /* set autoticks after ticks statement */ +{ + /* was there an explicit "ticks [side] off"? */ + if (toffside) + autoticks &= ~toffside; + /* was there an explicit list? (eg "ticks at ..." or "ticks from ...") */ + if (tlist) { + if (tside & (BOT|TOP)) + autoticks &= ~(BOT|TOP); + if (tside & (LEFT|RIGHT)) + autoticks &= ~(LEFT|RIGHT); + } + /* was there a side without a list? (eg "ticks left in") */ + if (tside && !tlist) { + if (tick_dir == IN) + autodir |= tside; + if (tside & (BOT|TOP)) + autoticks = (autoticks & ~(BOT|TOP)) | (tside & (BOT|TOP)); + if (tside & (LEFT|RIGHT)) + autoticks = (autoticks & ~(LEFT|RIGHT)) | (tside & (LEFT|RIGHT)); + } + tlist = tside = toffside = goffside = 0; + tick_dir = OUT; +} + +double modfloor(double f, double t) +{ + t = fabs(t); + return floor(f/t) * t; +} + +double modceil(double f, double t) +{ + t = fabs(t); + return ceil(f/t) * t; +} + +double xtmin, xtmax; /* range of ticks */ +double ytmin, ytmax; +double xquant, xmult; /* quantization & scale for auto x ticks */ +double yquant, ymult; +double lograt = 5; + +void do_autoticks(Obj *p) /* make set of ticks for default coord only */ +{ + double x, xl, xu, q; + + if (p == NULL) + return; + fprintf(tfd, "Autoticks:\t# x %g..%g, y %g..%g", + p->pt.x, p->pt1.x, p->pt.y, p->pt1.y); + fprintf(tfd, "; xt %g,%g, yt %g,%g, xq,xm = %g,%g, yq,ym = %g,%g\n", + xtmin, xtmax, ytmin, ytmax, xquant, xmult, yquant, ymult); + if ((autoticks & (BOT|TOP)) && p->pt1.x >= p->pt.x) { /* make x ticks */ + q = xquant; + xl = p->pt.x; + xu = p->pt1.x; + if (xl >= xu) + dflt_tick(xl); + else if ((p->log & XFLAG) && xu/xl >= lograt) { + for (x = q; x < xu; x *= 10) { + logtick(x, xl, xu); + if (xu/xl <= 100) { + logtick(2*x, xl, xu); + logtick(5*x, xl, xu); + } + } + } else { + xl = modceil(xtmin - q/100, q); + xu = modfloor(xtmax + q/100, q) + q/2; + for (x = xl; x <= xu; x += q) + dflt_tick(x); + } + tside = autoticks & (BOT|TOP); + ticklist(p, 0); + } + if ((autoticks & (LEFT|RIGHT)) && p->pt1.y >= p->pt.y) { /* make y ticks */ + q = yquant; + xl = p->pt.y; + xu = p->pt1.y; + if (xl >= xu) + dflt_tick(xl); + else if ((p->log & YFLAG) && xu/xl >= lograt) { + for (x = q; x < xu; x *= 10) { + logtick(x, xl, xu); + if (xu/xl <= 100) { + logtick(2*x, xl, xu); + logtick(5*x, xl, xu); + } + } + } else { + xl = modceil(ytmin - q/100, q); + xu = modfloor(ytmax + q/100, q) + q/2; + for (x = xl; x <= xu; x += q) + dflt_tick(x); + } + tside = autoticks & (LEFT|RIGHT); + ticklist(p, 0); + } +} + +void logtick(double v, double lb, double ub) +{ + float slop = 1.0; /* was 1.001 */ + + if (slop * lb <= v && ub >= slop * v) + dflt_tick(v); +} + +Obj *setauto(void) /* compute new min,max, and quant & mult */ +{ + Obj *p, *q; + + if ((q = lookup("lograt",0)) != NULL) + lograt = q->fval; + for (p = objlist; p; p = p->next) + if (p->type == NAME && strcmp(p->name,dflt_coord) == 0) + break; + if (p) { + if ((p->log & XFLAG) && p->pt1.x/p->pt.x >= lograt) + autolog(p, 'x'); + else + autoside(p, 'x'); + if ((p->log & YFLAG) && p->pt1.y/p->pt.y >= lograt) + autolog(p, 'y'); + else + autoside(p, 'y'); + } + return p; +} + +void autoside(Obj *p, int side) +{ + double r, s, d, ub, lb; + + if (side == 'x') { + xtmin = lb = p->pt.x; + xtmax = ub = p->pt1.x; + } else { + ytmin = lb = p->pt.y; + ytmax = ub = p->pt1.y; + } + if (ub <= lb) + return; /* cop out on little ranges */ + d = ub - lb; + r = s = 1; + while (d * s < 10) + s *= 10; + d *= s; + while (10 * r < d) + r *= 10; + if (r > d/3) + r /= 2; + else if (r <= d/6) + r *= 2; + if (side == 'x') { + xquant = r / s; + } else { + yquant = r / s; + } +} + +void autolog(Obj *p, int side) +{ + double r, s, t, ub, lb; + int flg; + + if (side == 'x') { + xtmin = lb = p->pt.x; + xtmax = ub = p->pt1.x; + flg = p->coord & XFLAG; + } else { + ytmin = lb = p->pt.y; + ytmax = ub = p->pt1.y; + flg = p->coord & YFLAG; + } + for (s = 1; lb * s < 1; s *= 10) + ; + lb *= s; + ub *= s; + for (r = 1; 10 * r < lb; r *= 10) + ; + for (t = 1; t < ub; t *= 10) + ; + if (side == 'x') + xquant = r / s; + else + yquant = r / s; + if (flg) + return; + if (ub / lb < 100) { + if (lb >= 5 * r) + r *= 5; + else if (lb >= 2 * r) + r *= 2; + if (ub * 5 <= t) + t /= 5; + else if (ub * 2 <= t) + t /= 2; + if (side == 'x') { + xtmin = r / s; + xtmax = t / s; + } else { + ytmin = r / s; + ytmax = t / s; + } + } +} + +void iterator(double from, double to, int op, double by, char *fmt) /* create an iterator */ +{ + double x; + + /* should validate limits, etc. */ + /* punt for now */ + + dprintf("iterate from %g to %g by %g, op = %c, fmt=%s\n", + from, to, by, op, fmt ? fmt : ""); + switch (op) { + case '+': + case ' ': + for (x = from; x <= to + (SLOP-1) * by; x += by) + if (fmt) + savetick(x, tostring(fmt)); + else + dflt_tick(x); + break; + case '-': + for (x = from; x >= to; x -= by) + if (fmt) + savetick(x, tostring(fmt)); + else + dflt_tick(x); + break; + case '*': + for (x = from; x <= SLOP * to; x *= by) + if (fmt) + savetick(x, tostring(fmt)); + else + dflt_tick(x); + break; + case '/': + for (x = from; x >= to; x /= by) + if (fmt) + savetick(x, tostring(fmt)); + else + dflt_tick(x); + break; + } + if (fmt) + free(fmt); +} + +void ticklist(Obj *p, int explicit) /* fire out the accumulated ticks */ + /* 1 => list, 0 => auto */ +{ + if (p == NULL) + return; + fprintf(tfd, "Ticks_%s:\n\tticklen = %g\n", p->name, ticklen); + print_ticks(TICKS, explicit, p, "ticklen", ""); +} + +void print_ticks(int type, int explicit, Obj *p, char *lenstr, char *descstr) +{ + int i, logflag, inside; + char buf[100]; + double tv; + + for (i = 0; i < ntick; i++) /* any ticks given explicitly? */ + if (tickstr[i] != NULL) + break; + if (i >= ntick && type == TICKS) /* no, so use values */ + for (i = 0; i < ntick; i++) { + if (tickval[i] >= 0.0) + sprintf(buf, "%g", tickval[i]); + else + sprintf(buf, "\\-%g", -tickval[i]); + tickstr[i] = tostring(buf); + } + else + for (i = 0; i < ntick; i++) { + if (tickstr[i] != NULL) { + sprintf(buf, tickstr[i], tickval[i]); + free(tickstr[i]); + tickstr[i] = tostring(buf); + } + } + logflag = sidelog(p->log, tside); + for (i = 0; i < ntick; i++) { + tv = tickval[i]; + halfrange(p, tside, tv); + if (logflag) { + if (tv <= 0.0) + ERROR "can't take log of tick value %g", tv FATAL; + logit(tv); + } + if (type == GRID) + inside = LEFT|RIGHT|TOP|BOT; + else if (explicit) + inside = (tick_dir == IN) ? tside : 0; + else + inside = autodir; + if (tside & BOT) + maketick(type, p->name, BOT, inside, tv, tickstr[i], lenstr, descstr); + if (tside & TOP) + maketick(type, p->name, TOP, inside, tv, tickstr[i], lenstr, descstr); + if (tside & LEFT) + maketick(type, p->name, LEFT, inside, tv, tickstr[i], lenstr, descstr); + if (tside & RIGHT) + maketick(type, p->name, RIGHT, inside, tv, tickstr[i], lenstr, descstr); + if (tickstr[i]) { + free(tickstr[i]); + tickstr[i] = NULL; + } + } + ntick = 0; +} + +void maketick(int type, char *name, int side, int inflag, double val, char *lab, char *lenstr, char *descstr) +{ + char *sidestr, *td; + + fprintf(tfd, "\tline %s ", descstr); + inflag &= side; + switch (side) { + case BOT: + case 0: + td = inflag ? "up" : "down"; + fprintf(tfd, "%s %s from (x_%s(%g),0)", td, lenstr, name, val); + break; + case TOP: + td = inflag ? "down" : "up"; + fprintf(tfd, "%s %s from (x_%s(%g),frameht)", td, lenstr, name, val); + break; + case LEFT: + td = inflag ? "right" : "left"; + fprintf(tfd, "%s %s from (0,y_%s(%g))", td, lenstr, name, val); + break; + case RIGHT: + td = inflag ? "left" : "right"; + fprintf(tfd, "%s %s from (framewid,y_%s(%g))", td, lenstr, name, val); + break; + } + fprintf(tfd, "\n"); + if (type == GRID && (side & goffside)) /* wanted no ticks on grid */ + return; + sidestr = tick_dir == IN ? "start" : "end"; + if (lab != NULL) { + /* BUG: should fix size of lab here */ + double wid = strlen(lab)/7.5 + (tick_dir == IN ? 0 : 0.1); /* estimate width at 15 chars/inch */ + switch (side) { + case BOT: case 0: + /* can drop "box invis" with new pic */ + fprintf(tfd, "\tbox invis \"%s\" ht .25 wid 0 with .n at last line.%s", + lab, sidestr); + break; + case TOP: + fprintf(tfd, "\tbox invis \"%s\" ht .2 wid 0 with .s at last line.%s", + lab, sidestr); + break; + case LEFT: + fprintf(tfd, "\t\"%s \" wid %.2f rjust at last line.%s", + lab, wid, sidestr); + break; + case RIGHT: + fprintf(tfd, "\t\" %s\" wid %.2f ljust at last line.%s", + lab, wid, sidestr); + break; + } + /* BUG: works only if "down x" comes before "at wherever" */ + lab_adjust(); + fprintf(tfd, "\n"); + } +} + +Attr *grid_desc = 0; + +void griddesc(Attr *a) +{ + grid_desc = a; +} + +void gridlist(Obj *p) +{ + char *framestr; + + if ((tside & (BOT|TOP)) || tside == 0) + framestr = "frameht"; + else + framestr = "framewid"; + fprintf(tfd, "Grid_%s:\n", p->name); + tick_dir = IN; + print_ticks(GRID, 0, p, framestr, desc_str(grid_desc)); + if (grid_desc) { + freeattr(grid_desc); + grid_desc = 0; + } +} + +char *desc_str(Attr *a) /* convert DOT to "dotted", etc. */ +{ + static char buf[50], *p; + + if (a == NULL) + return p = ""; + switch (a->type) { + case DOT: p = "dotted"; break; + case DASH: p = "dashed"; break; + case INVIS: p = "invis"; break; + default: p = ""; + } + if (a->fval != 0.0) { + sprintf(buf, "%s %g", p, a->fval); + return buf; + } else + return p; +} + +sidelog(int logflag, int side) /* figure out whether to scale a side */ +{ + if ((logflag & XFLAG) && ((side & (BOT|TOP)) || side == 0)) + return 1; + else if ((logflag & YFLAG) && (side & (LEFT|RIGHT))) + return 1; + else + return 0; +} -- cgit v1.2.3