/* scan.l - scanner for flex input */

/*
 * Copyright (c) 1987, the University of California
 * 
 * The United States Government has rights in this work pursuant to
 * contract no. DE-AC03-76SF00098 between the United States Department of
 * Energy and the University of California.
 * 
 * This program may be redistributed.  Enhancements and derivative works
 * may be created provided the new works, if made available to the general
 * public, are made available for use by anyone.
 */

%{
#include "flexdef.h"
#include "parse.h"

#define ACTION_ECHO fprintf( temp_action_file, "%s", yytext )
#define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" );

#undef YY_DECL
#define YY_DECL \
	int flexscan()

#define RETURNCHAR \
	yylval = yytext[0]; \
	return ( CHAR );

#define RETURNNAME \
	(void) strcpy( nmstr, yytext ); \
	return ( NAME );

#define PUT_BACK_STRING(str, start) \
	for ( i = strlen( str ) - 1; i >= start; --i ) \
	    unput(str[i])
%}

%x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE
%x FIRSTCCL CCL ACTION RECOVER BRACEERROR C_COMMENT C_COMMENT_2 ACTION_COMMENT
%x ACTION_STRING PERCENT_BRACE_ACTION

WS		[ \t]+

OPTWS		[ \t]*

NAME		[a-z_][a-z_0-9]*

SCNAME		{NAME}

ESCSEQ		\\([^\n]|0[0-9]{1,3})

%%
    static int bracelevel, didadef;
    int i, cclval;
    char nmdef[MAXLINE], myesc();

^{WS}.*\n		++linenum; ECHO; /* indented code */
^#.*\n			++linenum; ECHO; /* treat as a comment */
^"/*"			ECHO; BEGIN(C_COMMENT);
^"%s"(tart)?		return ( SCDECL );
^"%x"			return ( XSCDECL );
^"%{".*\n		++linenum; line_directive_out( stdout ); BEGIN(CODEBLOCK);
{WS}			return ( WHITESPACE );

^"%%".*			{
			sectnum = 2;
			line_directive_out( stdout );
			BEGIN(SECT2PROLOG);
			return ( SECTEND );
			}

^"%"[^sx{%].*\n		{
			fprintf( stderr,
			     "old-style lex command at line %d ignored:\n\t%s",
				 linenum, yytext );
			++linenum;
			}

^{NAME}			{
			(void) strcpy( nmstr, yytext );
			didadef = false;
			BEGIN(PICKUPDEF);
			}

{SCNAME}		RETURNNAME;
^{OPTWS}\n		++linenum; /* allows blank lines in section 1 */
\n			++linenum; return ( '\n' );
.			synerr( "illegal character" ); BEGIN(RECOVER);


<C_COMMENT>"*/"		ECHO; BEGIN(0);
<C_COMMENT>"*/".*\n	++linenum; ECHO; BEGIN(0);
<C_COMMENT>[^*\n]+	ECHO;
<C_COMMENT>"*"		ECHO;
<C_COMMENT>\n		++linenum; ECHO;

<CODEBLOCK>^"%}".*\n	++linenum; BEGIN(0);
<CODEBLOCK>.*\n		++linenum; ECHO;

<PICKUPDEF>{WS}		/* separates name and definition */

<PICKUPDEF>[^ \t\n].*	{
			(void) strcpy( nmdef, yytext );

			for ( i = strlen( nmdef ) - 1;
			      i >= 0 &&
			      nmdef[i] == ' ' || nmdef[i] == '\t';
			      --i )
			    ;

			nmdef[i + 1] = '\0';

                        ndinstal( nmstr, nmdef );
			didadef = true;
			}

<PICKUPDEF>\n		{
			if ( ! didadef )
			    synerr( "incomplete name definition" );
			BEGIN(0);
			++linenum;
			}

<RECOVER>.*\n		++linenum; BEGIN(0); RETURNNAME;


<SECT2PROLOG>.*\n/[^ \t\n]	{
			++linenum;
			ACTION_ECHO;
			MARK_END_OF_PROLOG;
			BEGIN(SECT2);
			}

<SECT2PROLOG>.*\n	++linenum; ACTION_ECHO;

<SECT2>^{OPTWS}\n	++linenum; /* allow blank lines in section 2 */

	/* this horrible mess of a rule matches indented lines which
	 * do not contain "/*".  We need to make the distinction because
	 * otherwise this rule will be taken instead of the rule which
	 * matches the beginning of comments like this one
	 */
<SECT2>^{WS}([^/\n]|"/"[^*\n])*("/"?)\n	{
			synerr( "indented code found outside of action" );
			++linenum;
			}

<SECT2>"<"		BEGIN(SC); return ( '<' );
<SECT2>^"^"		return ( '^' );
<SECT2>\"		BEGIN(QUOTE); return ( '"' );
<SECT2>"{"/[0-9]		BEGIN(NUM); return ( '{' );
<SECT2>"{"[^0-9\n][^}\n]*	BEGIN(BRACEERROR);
<SECT2>"$"/[ \t\n]	return ( '$' );

<SECT2>{WS}"%{"		{
			bracelevel = 1;
			BEGIN(PERCENT_BRACE_ACTION);
			return ( '\n' );
			}
<SECT2>{WS}"|".*\n	++linenum; return ( '\n' );

<SECT2>^{OPTWS}"/*"	ACTION_ECHO; BEGIN(C_COMMENT_2);

<SECT2>{WS}		{ /* needs to be separate from following rule due to
			   * bug with trailing context
			   */
			bracelevel = 0;
			BEGIN(ACTION);
			return ( '\n' );
			}

<SECT2>{OPTWS}/\n	{
			bracelevel = 0;
			BEGIN(ACTION);
			return ( '\n' );
			}

<SECT2>^{OPTWS}\n	++linenum; return ( '\n' );

<SECT2>^"%%".*		{
			/* guarantee that the SECT3 rule will have something
			 * to match
			 */
			yyless(1);
			sectnum = 3;
			BEGIN(SECT3);
			return ( EOF ); /* to stop the parser */
			}

<SECT2>"["([^\\\]\n]|{ESCSEQ})+"]"	{
			(void) strcpy( nmstr, yytext );

			/* check to see if we've already encountered this ccl */
			if ( (cclval = ccllookup( nmstr )) )
			    {
			    yylval = cclval;
			    ++cclreuse;
			    return ( PREVCCL );
			    }
			else
			    {
			    /* we fudge a bit.  We know that this ccl will
			     * soon be numbered as lastccl + 1 by cclinit
			     */
			    cclinstal( nmstr, lastccl + 1 );

			    /* push back everything but the leading bracket
			     * so the ccl can be rescanned
			     */
			    PUT_BACK_STRING(nmstr, 1);

			    BEGIN(FIRSTCCL);
			    return ( '[' );
			    }
			}

<SECT2>"{"{NAME}"}"	{
			register char *nmdefptr;
			char *ndlookup();

			(void) strcpy( nmstr, yytext );
			nmstr[yyleng - 1] = '\0';  /* chop trailing brace */

			/* lookup from "nmstr + 1" to chop leading brace */
			if ( ! (nmdefptr = ndlookup( nmstr + 1 )) )
			    synerr( "undefined {name}" );

			else
			    { /* push back name surrounded by ()'s */
			    unput(')');
			    PUT_BACK_STRING(nmdefptr, 0);
			    unput('(');
			    }
			}

<SECT2>[/|*+?.()]	return ( yytext[0] );
<SECT2>.		RETURNCHAR;
<SECT2>\n		++linenum; return ( '\n' );


<SC>","			return ( ',' );
<SC>">"			BEGIN(SECT2); return ( '>' );
<SC>">"/"^"		BEGIN(CARETISBOL); return ( '>' );
<SC>{SCNAME}		RETURNNAME;
<SC>.			synerr( "bad start condition name" );

<CARETISBOL>"^"		BEGIN(SECT2); return ( '^' );


<QUOTE>[^"\n]		RETURNCHAR;
<QUOTE>\"		BEGIN(SECT2); return ( '"' );

<QUOTE>\n		{
			synerr( "missing quote" );
			BEGIN(SECT2);
			++linenum;
			return ( '"' );
			}


<FIRSTCCL>"^"/[^-\n]	BEGIN(CCL); return ( '^' );
<FIRSTCCL>"^"/-		return ( '^' );
<FIRSTCCL>-		BEGIN(CCL); yylval = '-'; return ( CHAR );
<FIRSTCCL>.		BEGIN(CCL); RETURNCHAR;

<CCL>-/[^\]\n]		return ( '-' );
<CCL>[^\]\n]		RETURNCHAR;
<CCL>"]"			BEGIN(SECT2); return ( ']' );


<NUM>[0-9]+		{
			yylval = myctoi( yytext );
			return ( NUMBER );
			}

<NUM>","			return ( ',' );
<NUM>"}"			BEGIN(SECT2); return ( '}' );

<NUM>.			{
			synerr( "bad character inside {}'s" );
			BEGIN(SECT2);
			return ( '}' );
			}

<NUM>\n			{
			synerr( "missing }" );
			BEGIN(SECT2);
			++linenum;
			return ( '}' );
			}


<BRACEERROR>"}"		synerr( "bad name in {}'s" ); BEGIN(SECT2);
<BRACEERROR>\n		synerr( "missing }" ); ++linenum; BEGIN(SECT2);


<PERCENT_BRACE_ACTION>{OPTWS}"%}".*	bracelevel = 0;
<PERCENT_BRACE_ACTION>.*		ACTION_ECHO;
<PERCENT_BRACE_ACTION>\n		{
			++linenum;
			ACTION_ECHO;
			if ( bracelevel == 0 )
			    {
			    fputs( "\tYY_BREAK\n", temp_action_file );
			    BEGIN(SECT2);
			    }
			}

<ACTION>"{"		ACTION_ECHO; ++bracelevel;
<ACTION>"}"		ACTION_ECHO; --bracelevel;
<ACTION>[^{}"'/\n]+	ACTION_ECHO;
<ACTION>"/*"		ACTION_ECHO; BEGIN(ACTION_COMMENT);
<ACTION>"'"([^'\\\n]|\\.)*"'"	ACTION_ECHO; /* character constant */
<ACTION>\"		ACTION_ECHO; BEGIN(ACTION_STRING);
<ACTION>\n		{
			++linenum;
			ACTION_ECHO;
			if ( bracelevel == 0 )
			    {
			    fputs( "\tYY_BREAK\n", temp_action_file );
			    BEGIN(SECT2);
			    }
			}
<ACTION>.		ACTION_ECHO;

<ACTION_COMMENT>"*/"	ACTION_ECHO; BEGIN(ACTION);
<ACTION_COMMENT>[^*\n]+	ACTION_ECHO;
<ACTION_COMMENT>"*"	ACTION_ECHO;
<ACTION_COMMENT>\n	++linenum; ACTION_ECHO;
<ACTION_COMMENT>.	ACTION_ECHO;

<C_COMMENT_2>"*/"	ACTION_ECHO; BEGIN(SECT2);
<C_COMMENT_2>"*/".*\n	++linenum; ACTION_ECHO; BEGIN(SECT2);
<C_COMMENT_2>[^*\n]+	ACTION_ECHO;
<C_COMMENT_2>"*"	ACTION_ECHO;
<C_COMMENT_2>\n		++linenum; ACTION_ECHO;

<ACTION_STRING>[^"\\\n]+	ACTION_ECHO;
<ACTION_STRING>\\.	ACTION_ECHO;
<ACTION_STRING>\n	++linenum; ACTION_ECHO;
<ACTION_STRING>\"	ACTION_ECHO; BEGIN(ACTION);
<ACTION_STRING>.	ACTION_ECHO;


<SECT2,QUOTE,CCL>{ESCSEQ}	{
			yylval = myesc( yytext );
			return ( CHAR );
			}

<FIRSTCCL>{ESCSEQ}	{
			yylval = myesc( yytext );
			BEGIN(CCL);
			return ( CHAR );
			}


<SECT3>.|\n		{
			register int numchars;

			/* black magic - we know the names of a flex scanner's
			 * internal variables.  We cap the input buffer with
			 * an end-of-string and dump it to the output.
			 */
			YY_DO_BEFORE_SCAN; /* recover from setting up yytext */

#ifdef FLEX_FAST_SKEL
			fputs( yy_c_buf_p + 1, stdout );
#else
			yy_ch_buf[yy_e_buf_p + 1] = '\0';

			/* ignore the first character; it's the second '%'
			 * put back by the yyless(1) above
			 */
			fputs( yy_ch_buf + yy_c_buf_p + 1, stdout );
#endif

			/* if we don't do this, the data written by write()
			 * can get overwritten when stdout is finally flushed
			 */
			(void) fflush( stdout );

			while ( (numchars = read( fileno(yyin), yy_ch_buf,
						  YY_BUF_MAX )) > 0 )
			    (void) write( fileno(stdout), yy_ch_buf, numchars );
	
			if ( numchars < 0 )
			    flexerror( "fatal read error in section 3" );

			return ( EOF );
			}
%%
