home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 3
/
goldfish_volume_3.bin
/
files
/
dev
/
misc
/
adoc
/
source
/
adoc.yy
< prev
next >
Wrap
Text File
|
1995-04-13
|
17KB
|
543 lines
/*
* ADOC.YY -- Specification of the AUTODOC scanner
*
* (c)Copyright 1995 by Tobias Ferber, All Rights Reserved
*
* This file is part of ADOC.
*
* ADOC is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 1 of the License,
* or (at your option) any later version.
*
* ADOC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $VER: $Id: adoc.yy,v 1.21 1995/04/12 13:33:36 tf Exp $ */
%{
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "adoc.h"
#include "libfun.h"
#include "flist.h"
#include "debug.h"
/* In former versions of flex, yywrap had been defined as a macro */
#ifdef yywrap
#undef yywrap
#endif
/*
** local variables
*/
/* scanner's error stream */
static FILE *ferr;
/* current input line (used for warnings and error messages) */
static int scanner_line= 1;
/* set to the line in this file if an error occurs */
static int scanner_error= 0;
/* name of the current input file */
static char *scanner_infile= (char *)0;
static int scanner_flags;
/* pedanticness of warning method */
static int warn_mask;
/* the last function which has been reported by funreport() */
static char *report_fun= (char *)0;
/* string containing allowed characters for the comment type */
static char *fun_type;
static int have_heading= 0;
/*
** local functions
*/
static void funreport(const char *fmt, ...)
{
va_list argp;
va_start(argp,fmt);
{
char *fun= getfun( (char *)0 );
if(fun) {
if( !report_fun || strcmp(report_fun,fun) )
fprintf(ferr,"%s: In function `%s':\n",scanner_infile,fun);
}
else { /* !fun */
if(report_fun)
fprintf(ferr,"%s: At top level:\n",scanner_infile);
}
report_fun= fun;
}
fprintf(ferr,"%s: %d: ",scanner_infile,scanner_line);
vfprintf(ferr,(char *)fmt,argp);
fprintf(ferr,"\n");
fflush(ferr);
va_end(argp);
}
/* Check the availability of section keywords in a function description.
This procedure is called from the scanner at the end of a function */
static void funcheck(int line, ...)
{
char *fun, *arg;
va_list argp;
va_start(argp,line);
if( (fun= getfun((char *)0)) )
{
int missing= 0;
for(arg= va_arg(argp, char *); arg && *arg; arg= va_arg(argp, char *))
{
if( !getsec(arg) )
{
if(missing)
fprintf(ferr," `%s'",arg);
else /* first missing keyword */
{
if( !report_fun || strcmp(report_fun,fun) )
{
fprintf(ferr,"%s: In function `%s':\n",scanner_infile,fun);
report_fun= fun;
}
fprintf(ferr,"%s: %d: warning: missing keyword `%s'",scanner_infile,line,arg);
}
++missing;
}
}
if(missing)
fprintf(ferr,"\n");
}
va_end(argp);
}
/* remove white spaces from the left and/or right end of a string `s' */
static char *strip(char *s, char *lr)
{
char *l, *r, *t;
if( strchr(lr,'l') )
{
/* find the first non-white char `l' in `s' */
for(l=s; *l==' ' || *l=='\t'; l++) ;
}
else l=s;
if( strchr(lr,'r') )
{
/* find the last non-white char `r' in `s' */
for(t= r= l; *t; t++)
{
if(*t!=' ' && *t!='\t' && *t!='\r' && *t!='\n')
r= t;
}
if(r[0])
{
if(*r==' ' || *r=='\t' || *r=='\r' || *r=='\n')
*r= '\0';
else r[1]= '\0';
}
}
return l;
}
%}
BLACK [^\n\r\f\t ]
IDENT [a-zA-Z0-9\-_\.]+
AST "*"
NL (\r)|(\n)|(\r\n)|(\n\r)
/* the context has been moved to the rule section
because of problems with former versions of lex */
FUN_BEGIN [ \t]*{BLACK}{AST}{3}.{2}{AST}{1}" "
FUN_IDENT {IDENT}"/"{IDENT}
FUN_SECTION [ \t]*{BLACK}[ \t]+[A-Z]{2}[A-Z ]*
FUN_TEXT [ \t]*{BLACK}(([ \t].*)?)
FUN_END [ \t]*{BLACK}{AST}{2}
/*
* eXclusive scanner modes:
*
* INITIAL skip source-code
* EXPECT we just read a {FUN_BEGIN} and expect a {FUN_IDENT} now to enter <SKIP> mode
* SKIP skip the rest of the line, then enter <FUN> mode
* FUN collecting data in a comment section. {FUN_END} switches back to <INITIAL> mode again
*/
%x EXPECT SKIP FUN
%%
<INITIAL>^{FUN_BEGIN} { register char *s= yytext;
if( (scanner_flags & SCANNER_ALLOW_INDENTED_COMMENTS) || (*s!=' ' && *s!='\t') )
{
while(*s==' ' || *s=='\t')
++s;
if( strchr(fun_type,s[4]) || strchr(fun_type,s[5]) )
{
D(bug("line %ld: <%c%c>",scanner_line,s[4],s[5]));
BEGIN(EXPECT);
}
}
/* else: skip this indented comment */
}
<EXPECT>{FUN_IDENT} { D(bug("BEGIN function `%s'",yytext));
if( newfun((char *)yytext) )
{
funreport("error adding function `%s' -- not enough memory?",yytext);
return scanner_error= __LINE__;
}
have_heading= 0;
BEGIN(SKIP);
}
<EXPECT>. { BEGIN(INITIAL); }
<EXPECT>{NL} { ++scanner_line; BEGIN(INITIAL); }
<SKIP>.*{NL} { yyless(yyleng-1); BEGIN(FUN); }
/* There had been warnings because of a "dangerous trailing context"
when using <FUN>^{FUN_SECTION}/{NL} and <FUN>^{FUN_TEXT}/{NL}.
Note: we currently need the yyless() calls and put back the '\n' into
the input to make sure the `^' in is recognized afterwards */
<FUN>^{FUN_SECTION}{NL} { yyless(yyleng-1);
if( (scanner_flags & SCANNER_ALLOW_INDENTED_COMMENTS) || (yytext[0]!=' ' && yytext[0]!='\t') )
{
register char *s, *t;
for(s=yytext; *s==' ' || *s=='\t'; s++)
;
/* make a temporary copy of yytext */
if( (t=strdup(&s[1])) )
{
/* strip white chars on both sides of yytext */
s= strip(t,"lr");
D(bug("... section `%s'",s));
if( warn_mask & WARN_UNKNOWN_KEYWORDS )
{
if( !strarg(s,"NAME",
"SYNOPSIS",
"FUNCTION",
"INPUTS",
"RESULT",
"EXAMPLE",
"NOTES",
"BUGS",
"SEE ALSO", "") )
funreport("warning: unknown section keyword `%s'",s);
}
if( newsec(s) )
{
funreport("error adding section keyword `%s' -- not enough memory?",s);
return scanner_error= __LINE__;
}
have_heading= 1;
free(t);
}
else /* !t */
{
funreport("ran out of memory");
return scanner_error= __LINE__;
}
}
else
{
funreport("disallowed indentation before section heading");
return scanner_error= __LINE__;
}
}
<FUN>^{FUN_TEXT}{NL} { yyless(yyleng-1);
if( (scanner_flags & SCANNER_ALLOW_INDENTED_COMMENTS) || (yytext[0]!=' ' && yytext[0]!='\t') )
{
register char *s, *t;
for(s=yytext; *s==' ' || *s=='\t'; s++)
;
/* make a temporary copy of yytext and preserve
the correct indentation of the body text */
if( (s[1]=='\t') || (s[1]=='\0') || (s[1]=='\n') )
t= strdup(&s[1]);
else if( (t= strdup(s)) )
t[0]= ' ';
if(t)
{
/* remove (leading and) trailing white spaces - especially the {NL} */
s= strip(t, (scanner_flags & SCANNER_UNINDENT_BODYTEXT) ? "lr":"r");
if(!have_heading && *s)
{
if( newsec("") )
{
funreport("error creating untitled section -- not enough memory?");
return scanner_error= __LINE__;
}
if( warn_mask & WARN_UNTITLED_SECTION )
funreport("warning: untitled section");
have_heading= 1;
}
if( addtext(s) > 0 )
{
funreport("error adding text line -- not enough memory?");
return scanner_error= __LINE__;
}
free(t);
}
else /* !t */
{
funreport("ran out of memory");
return scanner_error= __LINE__;
}
}
else
{
funreport("disallowed indentation before section body");
return scanner_error= __LINE__;
}
}
<FUN>^{FUN_END}.*{NL} { D(bug("END function"));
if( warn_mask & WARN_MISSING_KEYWORDS )
{
funcheck( scanner_line, "NAME",
"SYNOPSIS",
"FUNCTION",
"INPUTS",
"RESULT",
"EXAMPLE",
"NOTES",
"BUGS",
"SEE ALSO", "");
}
yyless(yyleng-1);
BEGIN(INITIAL);
}
<FUN>^[ \t]*{NL} { if( warn_mask & WARN_BROKEN_COMMENTS )
funreport("warning: broken autodoc comment");
++scanner_line;
}
<FUN>. { if( warn_mask & WARN_STRANGE_TEXT )
funreport("warning: line does not match {FUN_TEXT}");
BEGIN(SKIP);
}
<INITIAL,EXPECT,SKIP,FUN>{NL} { scanner_line++; }
<INITIAL,EXPECT,SKIP,FUN>. /* eat up everything else in <*> mode */
<EXPECT,SKIP,FUN><<EOF>> { funreport("unterminated comment at the end of file");
return scanner_error= __LINE__;
}
<INITIAL><<EOF>> { D(bug("%d lines at <<EOF>>",scanner_line));
yyterminate();
}
%%
/* This function opens the scanner stream for the file `fname' and
initializes `scanner_infile' and `scanner_line'. It is called
by read_source() and yywrap(). If USE_YYRESTART is defined then
we use yyrestart(fp) to initialize `yyin' to the new FILE *.
otherwise we assign `yyin' the new FILE * and call YY_NEW_FILE. */
static int init_scanner(char *fname)
{
#if !defined(USE_YYRESTART)
static int num_calls= 0;
#endif
int result= 0;
if(fname)
{
if(*fname)
{
FILE *fp= fopen(fname,"r");
if(fp)
{
scanner_infile= fname;
if(yyin && (yyin !=stdin))
fclose(yyin);
#if defined(USE_YYRESTART)
yyrestart(fp);
#else
yyin= fp;
if(++num_calls > 1) YY_NEW_FILE;
#endif
}
else /* !fp */
{
fprintf(ferr,"scanner error: cannot read from `%s'\n",fname);
scanner_error= __LINE__;
result= 1;
}
}
else /* !*fname */
{
scanner_infile= "stdin";
if(yyin && (yyin !=stdin))
fclose(yyin);
#if defined(USE_YYRESTART)
yyrestart(stdin);
#else
yyin= stdin;
if(++num_calls > 1) YY_NEW_FILE;
#endif
}
}
else /* !fname */
{
/* We used to set `scanner_infile' to (char *)0 here but since
* the <<EOF>> rule which reports the unterminated comment is
* executed after the yywrap() call, we need to save the
* filename for this error message. */
/*scanner_infile= (char *)0;*/
result= 1;
}
/* reset the line counter */
scanner_line= 1;
#ifdef DEBUG
if(result == 0)
D(bug("--> `%s'", scanner_infile));
else
D(bug("--> end of scanner input queue"));
#endif /*DEBUG*/
return result;
}
/* In former versions of flex, yywrap had been defined as a macro */
#ifdef yywrap
#undef yywrap
#endif
/*
* yywrap() is called by yylex() each time the EOF is reached.
* It returns 0 if there is another file to be scanned or
* 1 if all scanning is done.
* Our yywrap() will take the next file from the flist queue.
*/
int yywrap()
{
int result= 1;
if( !scanner_error )
{
flist_nextfile();
result= init_scanner( flist_getname() );
}
return result;
}
int read_source(char *_fun_type, int _warn_mask, int flags)
{
int err= 0;
D(bug_enter("read_source"));
/* copy parameters to the local variables */
fun_type = _fun_type;
warn_mask = _warn_mask;
scanner_flags = flags;
/* initialize the error stream */
ferr= get_ferr();
/* initialize the scanner */
if( init_scanner( flist_getname() ) == 0 )
{
if(scanner_error == 0)
{
/* yylex() returns an integer != 0 if an error occured. */
err= yylex();
}
}
else
{
D(bug("ooops!? filename queue is empty?"));
}
/* errors from yywrap() are returned as negative values */
if(!err && scanner_error)
err= -scanner_error;
D(bug_leave("read_source (%s)",err ? "failed":"successful"));
return err;
}