%{
/*
 * Lexer for states.
 * Copyright (c) 1997-1998 Markku Rossi.
 *
 * Author: Markku Rossi <mtr@iki.fi>
 */

/*
 * This file is part of GNU Enscript.
 *
 * Enscript 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 3 of the License, or
 * (at your option) any later version.
 *
 * Enscript 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 Enscript.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * $Id: lex.l,v 1.1.1.1 2003/03/05 07:25:52 mtr Exp $
 */

#include "defs.h"
#include "gram.h"

static void eat_comment ();
static char *read_string ___P ((unsigned int *len_return));
static void read_regexp ___P ((Node *node));
%}

real	[+-]?[0-9]+\.[0-9]*|[+-]?\.[0-9]+
integer	[+-]?[0-9]+
symbol	[a-zA-Z_][a-zA-Z_0-9]*|\$.

%%

"/*" 		{ eat_comment (); }
[ \t\r\f]	{ ; }
\n		{ linenum++; }

\"		{ yylval.node = node_alloc (nSTRING);
		  yylval.node->u.str.data
	            = read_string (&yylval.node->u.str.len);
		  return tSTRING;
		}

'[^\\]' 	{ yylval.node = node_alloc (nINTEGER);
		  yylval.node->u.integer = yytext[1];
		  return tINTEGER;
		}

'\\.'		{ yylval.node = node_alloc (nINTEGER);
		  switch (yytext[2])
		    {
		    case 'n':
		      yylval.node->u.integer = '\n';
		      break;

		    case 't':
		      yylval.node->u.integer = '\t';
		      break;

		    case 'v':
		      yylval.node->u.integer = '\v';
		      break;

		    case 'b':
		      yylval.node->u.integer = '\b';
		      break;

		    case 'r':
		      yylval.node->u.integer = '\r';
		      break;

		    case 'f':
		      yylval.node->u.integer = '\f';
		      break;

		    case 'a':
		      yylval.node->u.integer = '\a';
		      break;

		    default:
		      yylval.node->u.integer = yytext[2];
		      break;
		    }

		  return tINTEGER;
		}

\/		{ yylval.node = node_alloc (nREGEXP);
		  read_regexp (yylval.node);
		  return tREGEXP;
		}

"BEGIN" 	{ return tBEGIN; }
"END" 		{ return tEND; }
"div" 		{ return tDIV; }
"else" 		{ return tELSE; }
"extends"	{ return tEXTENDS; }
"for" 		{ return tFOR; }
"if" 		{ return tIF; }
"local"		{ return tLOCAL; }
"namerules" 	{ return tNAMERULES; }
"return"	{ return tRETURN; }
"start" 	{ return tSTART; }
"startrules" 	{ return tSTARTRULES; }
"state" 	{ return tSTATE; }
"sub" 		{ return tSUB; }
"while"		{ return tWHILE; }

"==" 		{ return tEQ; }
"!=" 		{ return tNE; }
"<=" 		{ return tLE; }
">=" 		{ return tGE; }
"&&" 		{ return tAND; }
"||" 		{ return tOR; }
"++" 		{ return tPLUSPLUS; }
"--" 		{ return tMINUSMINUS; }
"+="		{ return tADDASSIGN; }
"-="		{ return tSUBASSIGN; }
"*="		{ return tMULASSIGN; }
"div="		{ return tDIVASSIGN; }

{real} 		{ yylval.node = node_alloc (nREAL);
		  yylval.node->u.real = atof (yytext);
		  return tREAL;
		}
{integer}	{ yylval.node = node_alloc (nINTEGER);
		  yylval.node->u.integer = atoi (yytext);
		  return tINTEGER;
		}
{symbol}	{ yylval.node = node_alloc (nSYMBOL);
		  yylval.node->u.sym = xstrdup (yytext);
		  return tSYMBOL;
		}

.		{ return yytext[0]; }

%%

static void
eat_comment ()
{
  int c;

  while ((c = input ()) != EOF)
    {
      if (c == '\n')
	linenum++;
      else if (c == '*')
	{
	  c = input ();
	  if (c == '/')
	    /* All done. */
	    return;

	  if (c == EOF)
	    {
	      yyerror (_("error: EOF in comment"));
	      break;
	    }
	  unput (c);
	}
    }
  yyerror (_("error: EOF in comment"));
}


int
yywrap ()
{
  return 1;
}

static char *
read_string (len_return)
     unsigned int *len_return;
{
  char *buf = NULL;
  char *buf2;
  int buflen = 0;
  int bufpos = 0;
  int ch;
  int done = 0;

  while (!done)
    {
      ch = input ();
      if (ch == '\n')
	linenum++;

      switch (ch)
        {
        case EOF:
        unexpected_eof:
          yyerror (_("error: EOF in string constant"));
	  done = 1;
          break;

        case '"':
          done = 1;
          break;

        case '\\':
          ch = input ();
          switch (ch)
            {
            case 'n':
              ch = '\n';
              break;

            case 't':
              ch = '\t';
              break;

            case 'v':
              ch = '\v';
              break;

            case 'b':
              ch = '\b';
              break;

            case 'r':
              ch = '\r';
              break;

            case 'f':
              ch = '\f';
              break;

            case 'a':
              ch = '\a';
              break;

            case EOF:
              goto unexpected_eof;
              break;

            default:
	      if (ch == '0')
		{
		  int i;
		  int val = 0;

		  for (i = 0; i < 3; i++)
		    {
		      ch = input ();
		      if ('0' <= ch && ch <= '7')
			val = val * 8 + ch - '0';
		      else
			{
			  unput (ch);
			  break;
			}
		    }
		  ch = val;
		}
              break;
            }
          /* FALLTHROUGH */

        default:
          if (bufpos >= buflen)
            {
              buflen += 1024;
              buf = (char *) xrealloc (buf, buflen);
            }
          buf[bufpos++] = ch;
          break;
        }
    }

  buf2 = (char *) xmalloc (bufpos + 1);
  memcpy (buf2, buf, bufpos);
  buf2[bufpos] = '\0';
  xfree (buf);

  *len_return = bufpos;

  return buf2;
}


static void
read_regexp (node)
     Node *node;
{
  char *buf = NULL;
  char *buf2;
  int buflen = 0;
  int bufpos = 0;
  int ch;
  int done = 0;

  while (!done)
    {
      ch = input ();
      switch (ch)
        {
        case EOF:
        unexpected_eof:
	  yyerror (_("error: EOF in regular expression"));
	  done = 1;
          break;

        case '/':
          done = 1;
          break;

        case '\\':
          ch = input ();
          switch (ch)
            {
	    case '\n':
	      /* Line break. */
	      linenum++;
	      continue;
	      break;

            case 'n':
              ch = '\n';
              break;

            case 'r':
              ch = '\r';
              break;

            case 'f':
              ch = '\f';
              break;

            case 't':
              ch = '\t';
              break;

	    case '/':
	    case '\\':
	      /* Quote these. */
	      break;

            case EOF:
              goto unexpected_eof;
              break;

            default:
	      if (ch == '0')
		{
		  int i;
		  int val = 0;

		  for (i = 0; i < 3; i++)
		    {
		      ch = input ();
		      if ('0' <= ch && ch <= '7')
			val = val * 8 + ch - '0';
		      else
			{
			  unput (ch);
			  break;
			}
		    }
		  ch = val;
		}
	      else
		{
		  /* Pass it through. */
		  unput (ch);
		  ch = '\\';
		}
	      break;
            }
          /* FALLTHROUGH */

        default:
          if (bufpos >= buflen)
            {
              buflen += 1024;
              buf = (char *) xrealloc (buf, buflen);
            }
          buf[bufpos++] = ch;
          break;
        }
    }

  /* Possible options. */
  done = 0;
  while (!done)
    {
      ch = input ();
      switch (ch)
	{
	case 'i':
	  /* Case-insensitive regular expression. */
	  node->u.re.flags |= fRE_CASE_INSENSITIVE;
	  break;

	default:
	  /* Unknown option => this belongs to the next token. */
	  unput (ch);
	  done = 1;
	  break;
	}
    }

  buf2 = (char *) xmalloc (bufpos + 1);
  memcpy (buf2, buf, bufpos);
  buf2[bufpos] = '\0';
  xfree (buf);

  node->u.re.data = buf2;
  node->u.re.len = bufpos;
}


/*
Local variables:
mode: c
End:
*/
