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

%{
/*-
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Vern Paxson.
 * 
 * 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.
 *
 * Redistribution and use in source and binary forms with or without
 * modification are permitted provided that: (1) source distributions retain
 * this entire copyright notice and comment, and (2) distributions including
 * binaries display the following acknowledgement:  ``This product includes
 * software developed by the University of California, Berkeley and its
 * contributors'' in the documentation or other materials provided with the
 * distribution and in all advertising materials mentioning features or use
 * of this software.  Neither the name of the University nor the names of
 * its contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* @Header: /home/daffy/u0/vern/flex/RCS/scan.l,v 2.56 95/04/24 12:17:19 vern Exp @ */

#include "flexdef.h"
#include "parse.h"

#define ACTION_ECHO add_action(yytext)

#define ACTION_ECHO2 add_off_action(bracelevel, &column, yytext)

#define ACTION_IFDEF(def, should_define) \
	{ \
	    if (should_define) \
		action_define(def, 1); \
	}

#define MARK_END_OF_PROLOG mark_prolog();

#define YY_DECL \
	int flexscan()

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

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

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

#define CHECK_REJECT(str) \
	if (all_upper(str)) \
	    reject = true;

#define CHECK_YYMORE(str) \
	if (all_lower(str)) \
	    yymore_used = true;

static void add_off_action(int, int *, const char *);

%}

%option caseless nodefault outfile="scan.c" stack noyy_top_state
%option nostdinit

%x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE
%x FIRSTCCL CCL ACTION RECOVER COMMENT ACTION_STRING PERCENT_BRACE_ACTION
%x OPTION LINEDIR

WS		[[:blank:]]+
OPTWS		[[:blank:]]*
NOT_WS		[^[:blank:]\n]

NL		\r?\n

NAME		([[:alpha:]_][[:alnum:]_-]*)
NOT_NAME	[^[:alpha:]_*\n]+

SCNAME		{NAME}

ESCSEQ		(\\([^\n]|[0-7]{1,3}|x[[:xdigit:]]{1,2}))

FIRST_CCL_CHAR	([^\\\n]|{ESCSEQ})
CCL_CHAR	([^\\\n\]]|{ESCSEQ})
CCL_EXPR	("[:"[[:alpha:]]+":]")

LEXOPT		[aceknopr]

%%
	static int bracelevel, didadef, indented_code, column;
	static int doing_rule_action = false;
	static int option_sense;

	int doing_codeblock = false;
	int i;
	Char nmdef[MAXLINE];


<INITIAL>{
	^{WS}		indented_code = true; BEGIN(CODEBLOCK);
	^"/*"		ACTION_ECHO; yy_push_state(COMMENT);
	^#{OPTWS}line{WS}	yy_push_state(LINEDIR);
	^"%s"{NAME}?	return SCDECL;
	^"%x"{NAME}?	return XSCDECL;
	^"%{".*{NL}	{
			++linenum;
			line_directive_out((FILE *) 0, 1);
			indented_code = false;
			BEGIN(CODEBLOCK);
			}

	{WS}		/* discard */

	^"%%".*		{
			sectnum = 2;
			bracelevel = 0;
			mark_defs1();
			line_directive_out((FILE *) 0, 1);
			BEGIN(SECT2PROLOG);
			return SECTEND;
			}

	^"%pointer".*{NL}	yytext_is_array = false; ++linenum;
	^"%array".*{NL}		yytext_is_array = true; ++linenum;

	^"%option"	BEGIN(OPTION); return OPTION_OP;

	^"%"{LEXOPT}{OPTWS}[[:digit:]]*{OPTWS}{NL}	++linenum; /* ignore */
	^"%"{LEXOPT}{WS}.*{NL}	++linenum;	/* ignore */

	^"%"[^sxaceknopr{}].*	synerr(_("unrecognized '%' directive"));

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

	{SCNAME}	RETURNNAME;
	^{OPTWS}{NL}	++linenum; /* allows blank lines in section 1 */
	{OPTWS}{NL}	ACTION_ECHO; ++linenum; /* maybe end of comment line */
}


<COMMENT>{
	"*/"		ACTION_ECHO; yy_pop_state();
	"*"		ACTION_ECHO;
	[^*\n]+		ACTION_ECHO;
	[^*\n]*{NL}	++linenum; ACTION_ECHO;
}

<LINEDIR>{
	\n		yy_pop_state();
	[[:digit:]]+	linenum = myctoi(yytext);

	\"[^"\n]*\"	{
			flex_free((void *) infilename);
			infilename = copy_string(yytext + 1);
			infilename[strlen(infilename) - 1] = '\0';
			}
	.		/* ignore spurious characters */
}

<CODEBLOCK>{
	^"%}".*{NL}	++linenum; BEGIN(INITIAL);

	{NAME}|{NOT_NAME}|.	ACTION_ECHO;

	{NL}		{
			++linenum;
			ACTION_ECHO;
			if (indented_code)
				BEGIN(INITIAL);
			}
}


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

	{NOT_WS}.*	{
			strcpy((char *) nmdef, yytext);

			/* Skip trailing whitespace. */
			for (i = (int) strlen((char *) nmdef) - 1;
			      i >= 0 && (nmdef[i] == ' ' || nmdef[i] == '\t');
			      --i) {
			    ;
		        }

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

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

	{NL}		{
			if (! didadef)
			    synerr(_("incomplete name definition"));
			BEGIN(INITIAL);
			++linenum;
			}
}

<OPTION>{
	{NL}		++linenum; BEGIN(INITIAL);
	{WS}		option_sense = true;

	"="		return '=';

	no		option_sense = ! option_sense;

	7bit		csize = option_sense ? 128 : 256;
	8bit		csize = option_sense ? 256 : 128;

	align		long_align = option_sense;
	always-interactive	{
			action_define("YY_ALWAYS_INTERACTIVE", option_sense);
			}
	array		yytext_is_array = option_sense;
	backup		backing_up_report = option_sense;
	batch		interactive = ! option_sense;
	"c++"		C_plus_plus = option_sense;
	caseful|case-sensitive		caseins = ! option_sense;
	caseless|case-insensitive	caseins = option_sense;
	debug		ddebug = option_sense;
	default		spprdflt = ! option_sense;
	ecs		useecs = option_sense;
	fast		{
			useecs = usemecs = false;
			use_read = fullspd = true;
			}
	full		{
			useecs = usemecs = false;
			use_read = fulltbl = true;
			}
	input		ACTION_IFDEF("YY_NO_INPUT", ! option_sense);
	interactive	interactive = option_sense;
	lex-compat	lex_compat = option_sense;
	main		{
			action_define("YY_MAIN", option_sense);
			do_yywrap = ! option_sense;
			}
	meta-ecs	usemecs = option_sense;
	never-interactive	{
			action_define("YY_NEVER_INTERACTIVE", option_sense);
			}
	perf-report	performance_report += option_sense ? 1 : -1;
	pointer		yytext_is_array = ! option_sense;
	read		use_read = option_sense;
	reject		reject_really_used = option_sense;
	stack		action_define("YY_STACK_USED", option_sense);
	stdinit		do_stdinit = option_sense;
	stdout		use_stdout = option_sense;
	unput		ACTION_IFDEF("YY_NO_UNPUT", ! option_sense);
	verbose		printstats = option_sense;
	warn		nowarn = ! option_sense;
	yylineno	do_yylineno = option_sense;
	yymore		yymore_really_used = option_sense;
	yywrap		do_yywrap = option_sense;

	yy_push_state	ACTION_IFDEF("YY_NO_PUSH_STATE", ! option_sense);
	yy_pop_state	ACTION_IFDEF("YY_NO_POP_STATE", ! option_sense);
	yy_top_state	ACTION_IFDEF("YY_NO_TOP_STATE", ! option_sense);

	yy_scan_buffer	ACTION_IFDEF("YY_NO_SCAN_BUFFER", ! option_sense);
	yy_scan_bytes	ACTION_IFDEF("YY_NO_SCAN_BYTES", ! option_sense);
	yy_scan_string	ACTION_IFDEF("YY_NO_SCAN_STRING", ! option_sense);

	outfile		return OPT_OUTFILE;
	prefix		return OPT_PREFIX;
	yyclass		return OPT_YYCLASS;

	\"[^"\n]*\"	{
			strcpy(nmstr, yytext + 1);
			nmstr[strlen(nmstr) - 1] = '\0';
			return NAME;
			}

	(([a-mo-z]|n[a-np-z])[[:alpha:]\-+]*)|.	{
			format_synerr(_("unrecognized %%option: %s"),
				yytext);
			BEGIN(RECOVER);
			}
}

<RECOVER>.*{NL}		++linenum; BEGIN(INITIAL);


<SECT2PROLOG>{
	^"%{".*	++bracelevel; yyless(2);	/* eat only %{ */
	^"%}".*	--bracelevel; yyless(2);	/* eat only %} */

	^{WS}.*	ACTION_ECHO;	/* indented code in prolog */

	^{NOT_WS}.*	{	/* non-indented code */
			if (bracelevel <= 0) {
			    /* not in %{ ... %} */
			    yyless(0);	/* put it all back */
			    yy_set_bol(1);
			    mark_prolog();
			    BEGIN(SECT2);
			} else
			    ACTION_ECHO;
			}

	.*		ACTION_ECHO;
	{NL}	++linenum; ACTION_ECHO;

	<<EOF>>		{
			mark_prolog();
			sectnum = 0;
			yyterminate(); /* to stop the parser */
			}
}

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

	^{OPTWS}"%{"	{
			indented_code = false;
			doing_codeblock = true;
			bracelevel = 1;
			BEGIN(PERCENT_BRACE_ACTION);
			}

	^{OPTWS}"<"	BEGIN(SC); return '<';
	^{OPTWS}"^"	return '^';
	\"		BEGIN(QUOTE); return '"';
	"{"/[[:digit:]]	BEGIN(NUM); return '{';
	"$"/([[:blank:]]|{NL})	return '$';

	{WS}"%{"		{
			bracelevel = 1;
			BEGIN(PERCENT_BRACE_ACTION);

			if (in_rule) {
			    doing_rule_action = true;
			    in_rule = false;
			    return '\n';
			}
			}
	{WS}"|".*{NL}	continued_action = true; ++linenum; return '\n';

	^{WS}"/*"	{
			yyless(yyleng - 2);	/* put back '/', '*' */
			column = 0;
			bracelevel = 0;
			continued_action = false;
			BEGIN(ACTION);
			}

	^{WS}		/* allow indented rules */

	{WS}		{
			/* This rule is separate from the one below because
			 * otherwise we get variable trailing context, so
			 * we can't build the scanner using -{f,F}.
			 */
			column = 0;
			bracelevel = 0;
			continued_action = false;
			BEGIN(ACTION);

			if (in_rule) {
			    doing_rule_action = true;
			    in_rule = false;
			    return '\n';
			}
			}

	{OPTWS}{NL}	{
			column = 0;
			bracelevel = 0;
			continued_action = false;
			BEGIN(ACTION);
			unput('\n');	/* so <ACTION> sees it */

			if (in_rule) {
			    doing_rule_action = true;
			    in_rule = false;
			    return '\n';
			}
			}

	^{OPTWS}"<<EOF>>"	|
	"<<EOF>>"	return EOF_OP;

	^"%%".*		{
			sectnum = 3;
			BEGIN(SECT3);
			yyterminate(); /* to stop the parser */
			}

	"["({FIRST_CCL_CHAR}|{CCL_EXPR})({CCL_CHAR}|{CCL_EXPR})*	{
			int cclval;

			strcpy(nmstr, yytext);

			/* Check to see if we've already encountered this
			 * ccl.
			 */
			if ((cclval = ccllookup((Char *) nmstr)) != 0) {
			    if (input() != ']')
				synerr(_("bad character class"));

			    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((Char *) nmstr, lastccl + 1);

			    /* Push back everything but the leading bracket
			     * so the ccl can be rescanned.
			     */
			    yyless(1);

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

	"{"{NAME}"}"	{
			Char *nmdefptr;

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

			if ((nmdefptr = ndlookup(nmstr)) == 0)
			    format_synerr(
					_("undefined definition {%s}"),
					    nmstr);

			else {
			    /* push back name surrounded by ()'s */
			    int len = (int) strlen((char *) nmdefptr);

			    if (lex_compat || nmdefptr[0] == '^' ||
				(len > 0 && nmdefptr[len - 1] == '$')) {
				    /* don't use ()'s after all */
				    PUT_BACK_STRING((char *) nmdefptr, 0);

				    if (nmdefptr[0] == '^')
					BEGIN(CARETISBOL);
			    } else {
				unput(')');
				PUT_BACK_STRING((char *) nmdefptr, 0);
				unput('(');
			    }
			}
			}

	[/|*+?.(){}]	return (unsigned char) yytext[0];
	.		RETURNCHAR;
}


<SC>{
	[,*]		return (unsigned char) yytext[0];
	">"		BEGIN(SECT2); return '>';
	">"/^		BEGIN(CARETISBOL); return '>';
	{SCNAME}	RETURNNAME;
	.		{
			format_synerr(_("bad <start condition>: %s"),
				yytext);
			}
}

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


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

	{NL}		{
			synerr(_("missing quote"));
			BEGIN(SECT2);
			++linenum;
			return '"';
			}
}


<FIRSTCCL>{
	"^"/[^-\]\n]	BEGIN(CCL); return '^';
	"^"/("-"|"]")	return '^';
	.		BEGIN(CCL); RETURNCHAR;
}

<CCL>{
	-/[^\]\n]	return '-';
	[^\]\n]		RETURNCHAR;
	"]"		BEGIN(SECT2); return ']';
	.|{NL}		{
			synerr(_("bad character class"));
			BEGIN(SECT2);
			return ']';
			}
}

<FIRSTCCL,CCL>{
	"[:alnum:]"	BEGIN(CCL); return CCE_ALNUM;
	"[:alpha:]"	BEGIN(CCL); return CCE_ALPHA;
	"[:blank:]"	BEGIN(CCL); return CCE_BLANK;
	"[:cntrl:]"	BEGIN(CCL); return CCE_CNTRL;
	"[:digit:]"	BEGIN(CCL); return CCE_DIGIT;
	"[:graph:]"	BEGIN(CCL); return CCE_GRAPH;
	"[:lower:]"	BEGIN(CCL); return CCE_LOWER;
	"[:print:]"	BEGIN(CCL); return CCE_PRINT;
	"[:punct:]"	BEGIN(CCL); return CCE_PUNCT;
	"[:space:]"	BEGIN(CCL); return CCE_SPACE;
	"[:upper:]"	BEGIN(CCL); return CCE_UPPER;
	"[:xdigit:]"	BEGIN(CCL); return CCE_XDIGIT;
	{CCL_EXPR}	{
			format_synerr(
				_("bad character class expression: %s"),
				    yytext);
			BEGIN(CCL); return CCE_ALNUM;
			}
}

<NUM>{
	[[:digit:]]+	{
			yylval = myctoi(yytext);
			return NUMBER;
			}

	","		return ',';
	"}"		BEGIN(SECT2); return '}';

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

	{NL}		{
			synerr(_("missing }"));
			BEGIN(SECT2);
			++linenum;
			return '}';
			}
}


<PERCENT_BRACE_ACTION>{
	{OPTWS}"%}".*		bracelevel = 0;

	<ACTION>"/*"		ACTION_ECHO2; yy_push_state(COMMENT);

	<CODEBLOCK,ACTION>{
		"reject"	{
			ACTION_ECHO;
			CHECK_REJECT(yytext);
			}
		"yymore"	{
			ACTION_ECHO;
			CHECK_YYMORE(yytext);
			}
	}

	{NAME}|{NOT_NAME}|.	ACTION_ECHO;
	{NL}		{
			++linenum;
			ACTION_ECHO;
			if (bracelevel == 0 ||
			     (doing_codeblock && indented_code)) {
				if (doing_rule_action)
				    add_ind_action(3, "YY_BREAK\n");

				doing_rule_action = doing_codeblock = false;
				BEGIN(SECT2);
			    }
			}
}

	/* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */
<ACTION>{
	"{"		ACTION_ECHO2; ++bracelevel;
	"}"		--bracelevel; ACTION_ECHO2; 
	[^[:alpha:]_{}"'/\n]+	ACTION_ECHO2;
	{NAME}		ACTION_ECHO2;
	"'"([^'\\\n]|\\.)*"'"	ACTION_ECHO2; /* character constant */
	\"		ACTION_ECHO; BEGIN(ACTION_STRING);
	{NL}		{
			++linenum;
			ACTION_ECHO2;
			if (bracelevel == 0) {
			    if (doing_rule_action)
				add_ind_action(3, "YY_BREAK\n");

			    doing_rule_action = false;
			    BEGIN(SECT2);
			}
			}
	.		ACTION_ECHO2;
}

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

<COMMENT,ACTION,ACTION_STRING><<EOF>>	{
			synerr(_("EOF encountered inside an action"));
			yyterminate();
			}


<SECT2,QUOTE,FIRSTCCL,CCL>{ESCSEQ}	{
			yylval = myesc((Char *) yytext);

			if (YY_START == FIRSTCCL)
			    BEGIN(CCL);

			return CHAR;
			}


<SECT3>{
	.*(\n?)		ECHO;
	<<EOF>>		sectnum = 0; yyterminate();
}

<*>.|\n			format_synerr(_("bad character: %s"), yytext);

%%

/*
 * Strip leading blanks from action-rules and add our own offset.
 * It is not quite as nice as indent, but is simple, and avoids putting
 * rule text (including curly-braces) in the first column.
 */
static void
add_off_action(int bracelevel, int *colp, const char *text)
{
    int was_zero = (*colp == 0);

    while (*text != '\0') {
	if (*text == '\n') {
	    *colp = 0;
	    break;
	} else if (*colp != 0 || (*text != '\t' && *text != ' ')) {
	    *colp = 1;
	    break;
	} else {
	    ++text;
	}
    }
    if (*text != '\0') {
	if (*colp != 0 && was_zero) {
	    add_ind_action(3 + bracelevel, text);
	} else {
	    add_action(text);
	}
    }
}

int
yywrap(void)
{
    if (--num_input_files > 0) {
	set_input_file(*++input_files);
	return 0;
    } else
	return 1;
}

/* set_input_file - open the given file (if NULL, stdin) for scanning */

void
set_input_file(char *file)
{
    if (file && strcmp(file, "-")) {
	infilename = copy_string(file);
	yyin = fopen(infilename, "r");

	if (yyin == NULL)
	    lerrsf(_("can't open %s"), file);
    } else {
	yyin = stdin;
	infilename = copy_string("<stdin>");
    }

    linenum = 1;
}

/* Wrapper routines for accessing the scanner's malloc routines. */

void *
flex_alloc(size_t size)
{
    return (void *) malloc(size);
}

void *
flex_realloc(void *ptr, size_t size)
{
    return (void *) realloc(ptr, size);
}

void
flex_free(void *ptr)
{
    if (ptr)
	free(ptr);
}
