/* Scan Bison Skeletons.                                       -*- C -*-

   Copyright (C) 2001-2007, 2009-2012 Free Software Foundation, Inc.

   This file is part of Bison, the GNU Compiler Compiler.

   This program 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.

   This program 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.  If not, see <http://www.gnu.org/licenses/>.  */

%option nodefault noyywrap noinput nounput never-interactive debug
%option prefix="skel_" outfile="lex.yy.c"

%{
/* Work around a bug in flex 2.5.31.  See Debian bug 333231
   <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>.  */
#undef skel_wrap
#define skel_wrap() 1

#define FLEX_PREFIX(Id) skel_ ## Id
#include "flex-scanner.h"

#include <dirname.h>
#include <error.h>
#include <quotearg.h>

#include "complain.h"
#include "getargs.h"
#include "files.h"
#include "scan-skel.h"

#define YY_DECL static int skel_lex (void)
YY_DECL;

#define QPUTS(String) \
   fputs (quotearg_style (c_quoting_style, String), yyout)

static void at_directive_perform (int at_directive_argc,
                                  char *at_directive_argv[],
                                  char **outnamep, int *out_linenop);
static void fail_for_at_directive_too_many_args (char const *at_directive_name);
static void fail_for_at_directive_too_few_args (char const *at_directive_name);
static void fail_for_invalid_at (char const *at);
%}

%x SC_AT_DIRECTIVE_ARGS
%x SC_AT_DIRECTIVE_SKIP_WS

%%

%{
  int out_lineno PACIFY_CC (= 0);
  char *outname = NULL;

  /* Currently, only the @warn, @complain, @fatal, @warn_at, @complain_at, and
     @fatal_at directives take multiple arguments, and the last three already
     can't take more than 7.  at_directive_argv[0] is the directive name.  */
  #define AT_DIRECTIVE_ARGC_MAX 8
  int at_directive_argc = 0;
  char *at_directive_argv[AT_DIRECTIVE_ARGC_MAX];
%}

"@@" fputc ('@', yyout);
"@{" fputc ('[', yyout);
"@}" fputc (']', yyout);
"@`" continue;  /* Used by b4_cat in ../data/bison.m4.  */
@\n  continue;

"@oline@"  fprintf (yyout, "%d", out_lineno + 1);
"@ofile@"  QPUTS (outname);

@[a-z_]+"(" {
  yytext[yyleng-1] = '\0';
  obstack_grow (&obstack_for_string, yytext, yyleng);
  at_directive_argv[at_directive_argc++] =
    obstack_finish (&obstack_for_string);
  BEGIN SC_AT_DIRECTIVE_ARGS;
}

  /* This pattern must not match more than the previous @ patterns. */
@[^@{}`(\n]*  fail_for_invalid_at (yytext);
\n            out_lineno++; ECHO;
[^@\n]+       ECHO;

<INITIAL><<EOF>> {
  if (outname)
    {
      free (outname);
      xfclose (yyout);
    }
  return EOF;
}

<SC_AT_DIRECTIVE_ARGS>
{
  [^@]+  STRING_GROW;

  "@@"   obstack_1grow (&obstack_for_string, '@');
  "@{"   obstack_1grow (&obstack_for_string, '[');
  "@}"   obstack_1grow (&obstack_for_string, ']');
  "@`"   continue; /* For starting an argument that begins with whitespace. */
  @\n    continue;

  @[,)] {
    if (at_directive_argc >= AT_DIRECTIVE_ARGC_MAX)
      fail_for_at_directive_too_many_args (at_directive_argv[0]);

    obstack_1grow (&obstack_for_string, '\0');
    at_directive_argv[at_directive_argc++] =
      obstack_finish (&obstack_for_string);

    /* Like M4, skip whitespace after a comma.  */
    if (yytext[1] == ',')
      BEGIN SC_AT_DIRECTIVE_SKIP_WS;
    else
      {
        at_directive_perform (at_directive_argc, at_directive_argv,
                              &outname, &out_lineno);
        obstack_free (&obstack_for_string, at_directive_argv[0]);
        at_directive_argc = 0;
        BEGIN INITIAL;
      }
  }

  @.?  fail_for_invalid_at (yytext);
}

<SC_AT_DIRECTIVE_SKIP_WS>
{
  [ \t\r\n]    continue;
  .            { yyless (0); BEGIN SC_AT_DIRECTIVE_ARGS; }
}

<SC_AT_DIRECTIVE_ARGS,SC_AT_DIRECTIVE_SKIP_WS>
{
  <<EOF>> {
    fatal (_("unclosed %s directive in skeleton"), at_directive_argv[0]);
  }
}

%%

/*------------------------.
| Scan a Bison skeleton.  |
`------------------------*/

void
scan_skel (FILE *in)
{
  static bool initialized = false;
  if (!initialized)
    {
      initialized = true;
      obstack_init (&obstack_for_string);
    }
  skel_in = in;
  skel__flex_debug = trace_flag & trace_skeleton;
  skel_lex ();
}

void
skel_scanner_free (void)
{
  obstack_free (&obstack_for_string, 0);
  /* Reclaim Flex's buffers.  */
  yylex_destroy ();
}

static void
at_directive_perform (int at_directive_argc,
                      char *at_directive_argv[],
                      char **outnamep, int *out_linenop)
{
  if (0 == strcmp (at_directive_argv[0], "@basename"))
    {
      if (at_directive_argc > 2)
        fail_for_at_directive_too_many_args (at_directive_argv[0]);
      fputs (last_component (at_directive_argv[1]), yyout);
    }
  else if (0 == strcmp (at_directive_argv[0], "@warn")
           || 0 == strcmp (at_directive_argv[0], "@complain")
           || 0 == strcmp (at_directive_argv[0], "@fatal"))
    {
      void (*func)(char const *, ...);
      switch (at_directive_argv[0][1])
        {
          case 'w': func = warn; break;
          case 'c': func = complain; break;
          case 'f': func = fatal; break;
          default: aver (false); break;
        }
      switch (at_directive_argc)
        {
          case 2:
            func (_(at_directive_argv[1]));
            break;
          case 3:
            func (_(at_directive_argv[1]), at_directive_argv[2]);
            break;
          case 4:
            func (_(at_directive_argv[1]), at_directive_argv[2],
                  at_directive_argv[3]);
            break;
          case 5:
            func (_(at_directive_argv[1]), at_directive_argv[2],
                  at_directive_argv[3], at_directive_argv[4]);
            break;
          case 6:
            func (_(at_directive_argv[1]), at_directive_argv[2],
                  at_directive_argv[3], at_directive_argv[4],
                  at_directive_argv[5]);
            break;
          default:
            fail_for_at_directive_too_many_args (at_directive_argv[0]);
            break;
        }
    }
  else if (0 == strcmp (at_directive_argv[0], "@warn_at")
           || 0 == strcmp (at_directive_argv[0], "@complain_at")
           || 0 == strcmp (at_directive_argv[0], "@fatal_at"))
    {
      void (*func)(location, char const *, ...);
      location loc;
      if (at_directive_argc < 4)
        fail_for_at_directive_too_few_args (at_directive_argv[0]);
      switch (at_directive_argv[0][1])
        {
          case 'w': func = warn_at; break;
          case 'c': func = complain_at; break;
          case 'f': func = fatal_at; break;
          default: aver (false); break;
        }
      boundary_set_from_string (&loc.start, at_directive_argv[1]);
      boundary_set_from_string (&loc.end, at_directive_argv[2]);
      switch (at_directive_argc)
        {
          case 4:
            func (loc, _(at_directive_argv[3]));
            break;
          case 5:
            func (loc, _(at_directive_argv[3]), at_directive_argv[4]);
            break;
          case 6:
            func (loc, _(at_directive_argv[3]), at_directive_argv[4],
                  at_directive_argv[5]);
            break;
          case 7:
            func (loc, _(at_directive_argv[3]), at_directive_argv[4],
                  at_directive_argv[5], at_directive_argv[6]);
            break;
          case 8:
            func (loc, _(at_directive_argv[3]), at_directive_argv[4],
                  at_directive_argv[5], at_directive_argv[6],
                  at_directive_argv[7]);
            break;
          default:
            fail_for_at_directive_too_many_args (at_directive_argv[0]);
            break;
        }
    }
  else if (0 == strcmp (at_directive_argv[0], "@output"))
    {
      if (at_directive_argc > 2)
        fail_for_at_directive_too_many_args (at_directive_argv[0]);
      if (*outnamep)
        {
          free (*outnamep);
          xfclose (yyout);
        }
      *outnamep = xstrdup (at_directive_argv[1]);
      output_file_name_check (outnamep);
      yyout = xfopen (*outnamep, "w");
      *out_linenop = 1;
    }
  else
    fail_for_invalid_at (at_directive_argv[0]);
}

static void
fail_for_at_directive_too_few_args (char const *at_directive_name)
{
  fatal (_("too few arguments for %s directive in skeleton"),
         at_directive_name);
}

static void
fail_for_at_directive_too_many_args (char const *at_directive_name)
{
  fatal (_("too many arguments for %s directive in skeleton"),
         at_directive_name);
}

static void
fail_for_invalid_at (char const *at)
{
  fatal ("invalid @ in skeleton: %s", at);
}