summaryrefslogtreecommitdiff
path: root/support/rail/rail.c
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/rail/rail.c
Initial commit
Diffstat (limited to 'support/rail/rail.c')
-rw-r--r--support/rail/rail.c726
1 files changed, 726 insertions, 0 deletions
diff --git a/support/rail/rail.c b/support/rail/rail.c
new file mode 100644
index 0000000000..c0f0b3851e
--- /dev/null
+++ b/support/rail/rail.c
@@ -0,0 +1,726 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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;i<body->nlist;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;i<body2->nlist;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;i<j;i++,j--) {
+ tmp=body->list[i];
+ body->list[i]=body->list[j];
+ body->list[j]=tmp;
+ }
+ }
+
+ for(i=0;i<body->nlist;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;i<indent;i++)
+ fprintf(outf,". ");
+
+ if(body==NULL)
+ fprintf(outf,"NULL\n");
+ else {
+ switch(body->kind) {
+
+ 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;i<body->nlist;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;i<body->nlist;i++)
+ fmtbody(body->list[i],"",arrow);
+ break;
+
+ case BAR:
+ next="\\rail@bar\n";
+ for(i=0;i<body->nlist;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;i<body->nlist;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;i<body->nlist;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);
+ }
+}
+