/* * rail.c - railroad diagram utility * * 09-Jul-1990 L. Rooijakkers * 09-Oct-1990 L. Rooijakkers * 07-Feb-1991 L. Rooijakkers indexing, embedded options * 08-Feb-1991 L. Rooijakkers small mods for version 1.0 #0 * 12-Feb-1991 L. Rooijakkers minor portability fixes * 28-Jun-1994 K. Barthelmann minor changes * 12-Jan-1997 K. Barthelmann minor portability improvements * 19-May-1998 J. Olsson Added suport for arrow heads. * */ #ifndef lint static char SccsId[]="@(#)rail 26-Jul-1998"; #endif #define USAGE "usage: %s [-+acdit] [file]\n" #include #include #include #include "patchlevel.h" #include "rail.h" #include "gram.h" static char *myname; /* program name */ static char *file; /* input file */ extern FILE *yyin; /* lex input stream */ static char *outfile; /* output file */ FILE *outf; /* output stream */ static int newline; /* stdout requires a newline */ static IDTYPE *idlist; /* identifier list */ IDTYPE *errorid; /* identifier for errors */ /* options */ int altstar; /* alternate star */ int arrowheads; /* produce arrowheads on the rails */ static int chkgram; /* check grammar */ static int treelist; /* list rule trees */ static int genindex; /* generate index entry for left hand sides */ #ifdef YYDEBUG extern int yydebug; /* show yacc debugging */ #endif int anonymous; /* anonymous rules */ main(argc,argv) unsigned argc; char *argv[]; { char *arg, **argp; unsigned len; printf("This is Rail, Version %s #%d\n",VERSION,PATCHLEVEL); newline=0; myname=argv[0]; for(argp = &argv[1];(arg = *argp)!=NULL;argp++) { if(*arg!='-' && *arg!='+') break; if(setopt(*arg,arg+1)==0) usage(); } if(arg!=NULL && *++argp!=NULL) usage(); if(arg==NULL) { file="stdin"; outfile="stdout"; yyin=stdin; outf=stdout; } else { len=strlen(arg)+5; file=mcheck(malloc(len)); outfile=mcheck(malloc(len)); strcat(strcpy(file,arg),".rai"); strcat(strcpy(outfile,arg),".rao"); if((yyin=fopen(file,"r"))==NULL) { perror(file); exit(1); } if((outf=fopen(outfile,"w"))==NULL) { perror(outfile); exit(1); } } printf("(%s",file); newline=1; line=1; fprintf(outf,"%% This file was generated by '%s' from '%s'\n",myname,file); if(yyparse()!=0) exit(1); printf(")\n"); newline=0; file=NULL; if(chkgram) { checkdefs(); if(anonymous) error("unnamed rules are present",(char *)NULL); } exit(0); /*NOTREACHED*/ } int setopt(c,s) char c, *s; { int set; set = c=='-'; while(*s!='\0') { switch(*s++) { case 'a': altstar=set; break; case 'c': chkgram=set; break; case 'd': #ifdef YYDEBUG yydebug=set; #endif break; case 'i': genindex=set; break; case 't': treelist=set; break; case 'h': arrowheads=set; break; default: return 0; } } return 1; } usage() { fprintf(stderr,USAGE,myname); exit(1); } /* error routine for yyparse() */ yyerror(s) char *s; { fatal("%s",s); } /* wrap-up routine for yylex() */ yywrap() { return(1); } /* check for non-NULL pointer */ char *mcheck(s) char *s; { if(s==NULL) fatal("out of memory",(char *)NULL); return(s); } /* make a new body */ BODYTYPE *newbody(kind,body1,body2) int kind; BODYTYPE *body1, *body2; { BODYTYPE *body; body=(BODYTYPE *)mcheck(malloc(sizeof(BODYTYPE))); body->kind=kind; body->nlist=0; body->done=0; body->id=NULL; body->text=NULL; body->annot=NULL; if(body1!=NULL) body->list[body->nlist++]=body1; if(body2!=NULL) body->list[body->nlist++]=body2; return(body); } /* free a body recursively */ freebody(body) BODYTYPE *body; { int i; if(body==NULL) return; for(i=0;inlist;i++) freebody(body->list[i]); if(body->text!=NULL) { /* should free text here */ } free((char *)body); } /* test if a body is empty */ int isemptybody(body) BODYTYPE *body; { return(body==NULL || body->kind==EMPTY); } /* add to a body list */ static addlist(body1,body2) BODYTYPE *body1, *body2; { if(body1->nlist>=MAXLIST) { yyerror("list too long"); } else body1->list[body1->nlist++]=body2; } /* add two body lists (for CAT, BAR, PLUS) */ BODYTYPE *addbody(kind,body1,body2) int kind; BODYTYPE *body1, *body2; { BODYTYPE *body; int i; if(body1->kind==kind && !(kind==BAR && body1->done) ) { body=body1; } else { body=newbody(kind,NULLBODY,NULLBODY); addlist(body,body1); } if(body2->kind==kind && !(kind==BAR && body2->done) ) { for(i=0;inlist;i++) addlist(body,body2->list[i]); free((char *)body2); } else { addlist(body,body2); } return(body); } /* reverse all concatenations (for PLUS) */ BODYTYPE *revbody(body) BODYTYPE *body; { int i,j; BODYTYPE *tmp; if(body->kind==CAT) { for(i=0,j=body->nlist-1;ilist[i]; body->list[i]=body->list[j]; body->list[j]=tmp; } } for(i=0;inlist;i++) body->list[i]=revbody(body->list[i]); return(body); } /* print a body for debugging */ prtbody(indent,body) int indent; BODYTYPE *body; { int i; fprintf(outf,"%% "); for(i=0;ikind) { case EMPTY: fprintf(outf,"EMPTY"); break; case CAT: fprintf(outf,"CAT"); break; case BAR: fprintf(outf,"BAR"); break; case PLUS: fprintf(outf,"PLUS"); break; case ANNOTE: fprintf(outf,"ANNOTE [%s]",body->text); break; case IDENT: fprintf(outf,"IDENT %s",body->id->name); break; case CR: fprintf(outf,"CR"); break; case STRNG: fprintf(outf,"STRNG '%s'",body->text); break; default: fprintf(outf,"UNKNOWN"); break; } fprintf(outf," y=%d nexty=%d\n",body->ystart,body->ynext); for(i=0;inlist;i++) prtbody(indent+1,body->list[i]); } } /* output a body */ outbody(id,body) IDTYPE *id; BODYTYPE *body; { posbody(body,0); if(treelist) prtbody(0,body); fprintf(outf,"\\rail@begin{%d}{",body->ynext); if(id!=NULL) { fprintf(outf,"%s",id->name); } fprintf(outf,"}\n"); if (arrowheads) { fmtbody(body,"",RIGHT_ARROW); fprintf(outf,"\\rail@vend\n"); } else { fmtbody(body,"",NO_ARROW); fprintf(outf,"\\rail@end\n"); } } /* format a body */ fmtbody(body,cent,arrow) BODYTYPE *body; char *cent; char arrow; { BODYTYPE *body1; int i; char *next; switch(body->kind) { case EMPTY: break; case CAT: for(i=0;inlist;i++) fmtbody(body->list[i],"",arrow); break; case BAR: next="\\rail@bar\n"; for(i=0;inlist;i++) { body1=body->list[i]; fprintf(outf,next,body1->ystart); next="\\rail@nextbar{%d}\n"; fmtbody(body1,"",arrow); } fprintf(outf,"\\rail@endbar\n"); break; case PLUS: fprintf(outf,"\\rail@plus\n"); if (arrow == NO_ARROW) fmtbody(body->list[0],"",arrow); else if (body->list[0]->kind == EMPTY) { arrow = arrow == RIGHT_ARROW ? LEFT_ARROW : RIGHT_ARROW; fmtbody(body->list[0],"",arrow); } else { fmtbody(body->list[0],"",arrow); arrow = arrow == RIGHT_ARROW ? LEFT_ARROW : RIGHT_ARROW; } body1=body->list[1]; fprintf(outf,"\\rail@nextplus{%d}\n",body1->ystart); fmtbody(body1,"c",arrow); fprintf(outf,"\\rail@endplus\n"); break; case ANNOTE: fprintf(outf,"\\rail@annote[%s]\n",body->text); break; case IDENT: if(body->id->kind==TERM) if (arrow == NO_ARROW) fprintf(outf,"\\rail@%stoken{%s}",cent,body->id->name); else fprintf(outf,"\\rail@%c%stoken{%s}",arrow,cent,body->id->name); else if (arrow == NO_ARROW) fprintf(outf,"\\rail@%snont{%s}",cent,body->id->name); else fprintf(outf,"\\rail@%c%snont{%s}",arrow,cent,body->id->name); fprintf(outf,"[%s]\n",body->annot!=NULL?body->annot:""); break; case CR: fprintf(outf,"\\rail@cr{%d}\n",body->ynext-1); break; case STRNG: if (arrow == NO_ARROW) fprintf(outf,"\\rail@%sterm{%s}",cent,body->text); else fprintf(outf,"\\rail@%c%sterm{%s}",arrow,cent,body->text); fprintf(outf,"[%s]\n",body->annot!=NULL?body->annot:""); break; default: fprintf(outf,"\\rail@unknown\n"); break; } } /* position body (fill in height and ystart) */ posbody(body,ystart) BODYTYPE *body; { BODYTYPE *body1; int i; switch(body->kind) { case CAT: body->ystart=ystart; body->ynext=ystart+1; for(i=0;inlist;i++) { body1=body->list[i]; if(body1->kind==CR) { body1->ystart=ystart; body1->ynext=body->ynext+2; ystart=body1->ynext-1; } else posbody(body1,ystart); if(body1->ynext>body->ynext) body->ynext=body1->ynext; } break; case BAR: case PLUS: body->ystart=ystart; body->ynext=ystart+1; for(i=0;inlist;i++) { body1=body->list[i]; posbody(body1,ystart); ystart=body1->ynext; if(body1->ynext>body->ynext) body->ynext=body1->ynext; } break; case CR: body->ystart=ystart; body->ynext=ystart+3; break; case EMPTY: case ANNOTE: case IDENT: case STRNG: default: body->ystart=ystart; body->ynext=ystart+1; break; } } /* output an index entry */ outindex(id) IDTYPE *id; { if(id!=NULL) fprintf(outf,"\\rail@index{%s}\n",id->name); } /* make a new rule list */ RULETYPE *newrule(id,body) IDTYPE *id; BODYTYPE *body; { RULETYPE *rule; rule=(RULETYPE *)mcheck(malloc(sizeof(RULETYPE))); rule->id=id; rule->body=body; rule->next=NULL; return(rule); } /* free a rule list */ freerule(rule) RULETYPE *rule; { RULETYPE *rulep; while(rule!=NULL) { rulep=rule->next; freebody(rule->body); free((char *)rule); rule=rulep; } } /* add two rule lists */ RULETYPE *addrule(rule1,rule2) RULETYPE *rule1, *rule2; { RULETYPE *rulep; if(rule1==NULL) return(rule2); for(rulep=rule1;rulep->next!=NULL;rulep=rulep->next) ; rulep->next=rule2; return(rule1); } /* output a rule list */ outrule(rule) RULETYPE *rule; { while(rule!=NULL) { if(genindex) outindex(rule->id); outbody(rule->id,rule->body); rule=rule->next; } } /* look up an identifier */ IDTYPE *lookup(name) char *name; { IDTYPE *idp, **idq; for(idq = &idlist;(idp = *idq)!=NULL;idq = &idp->next) if(strcmp(name,idp->name)==0) return(idp); idp=(IDTYPE *)mcheck(malloc(sizeof(IDTYPE))); idp->name=mcheck(malloc((unsigned)strlen(name)+1)); strcpy(idp->name,name); idp->next=NULL; idp->kind=UNKNOWN; *idq=idp; return(idp); } /* delete an identifier */ delete(id) IDTYPE *id; { IDTYPE *idp, **idq; for(idq = &idlist;(idp = *idq)!=NULL;idq = &idp->next) { if(idp==id) { *idq = idp->next; free(idp->name); free((char *)idp); return; } } } /* check that there are no undefined identifiers */ checkdefs() { IDTYPE *id; for(id=idlist;id!=NULL;id=id->next) if(id->kind==TOKEN) undef(id); } /* complain about an undefined identifier */ undef(id) IDTYPE *id; { if(chkgram) error("undefined identifier '%s'",id->name); } /* complain about a redefined identifier */ redef(id) IDTYPE *id; { if(chkgram) error("redefined identifier '%s'",id->name); } /* display an error */ error(f,s) char *f, *s; { if(newline) { printf("\n"); newline=0; } if(file!=NULL) fprintf(stderr,"%s, line %u: ",file,line); fprintf(stderr,f,s); if(errorid!=NULL) fprintf(stderr," in rule '%s'",errorid->name); fprintf(stderr,"\n"); } fatal(f,s) char *f,*s; { error(f,s); if(outf!=NULL) { fclose(outf); unlink(outfile); } }