/*-----------------------------------------------------------------------
  SDCC.lex - lexical analyser for use with sdcc (free open source
  compiler for 8/16 bit microcontrollers)
  Written by : Sandeep Dutta . sandeep.dutta@usa.net (1997)

  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 2, 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, write to the Free Software
  Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

  In other words, you are welcome to use, share and improve this program.
  You are forbidden to forbid anyone else to use, share and improve
  what you give them.   Help stamp out software-hoarding!
-------------------------------------------------------------------------*/

B       [0-1]
D       [0-9]
L       [a-zA-Z_$]
H       [a-fA-F0-9]
E       [Ee][+-]?{D}+
BE      [Pp][+-]?{D}+
FS      (f|F|l|L)
IS      (u|U|l|L)*
CP      (L|u|U)
HASH    (#|%:)

%{
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "common.h"
#include "newalloc.h"
#include "dbuf_string.h"
/* Needed by flex 2.5.4 on NetBSD 5.0.1 sparc64 */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/* MSVC has no unistd.h but has read() declaration in io.h */
#if defined(_MSC_VER)
# include <io.h>
#endif

#define TKEYWORD(token) return (isTargetKeyword(yytext) ? token :\
                                check_type())

#define TKEYWORD99(token) return (options.std_c99 ? token : check_type())

int column = 0;         /* current column */

/* global definitions */
char *lexFilename;
int lexLineno = 1;

/* local definitions */
static struct dbuf_s asmbuff; /* reusable _asm buffer */

/* forward declarations */
int yyerror (char *s);
static const char *stringLiteral (char);
static void count (void);
static void count_char (int);
static int process_pragma (const char *);
static int check_type (void);
static void checkCurrFile (const char *s);
%}

%x asm
%%
"__asm"                {
  count ();
  if (asmbuff.buf == NULL)
    dbuf_init (&asmbuff, INITIAL_INLINEASM);
  else
    dbuf_set_length (&asmbuff, 0);

  BEGIN (asm);
}
<asm>"__endasm"        {
  count ();
  yylval.yystr = dbuf_c_str (&asmbuff);
  BEGIN (INITIAL);
  return INLINEASM;
}
<asm>\n                 {
  count ();
  dbuf_append_char(&asmbuff, *yytext);
}
<asm>.                  {
  dbuf_append_char(&asmbuff, *yytext);
}
"__asm__"               { count (); return ASM; }
"__at"                  { count (); TKEYWORD (AT); }
"auto"                  { count (); return AUTO; }
"__bit"                 { count (); TKEYWORD (BIT); }
"_Bool"                 { count (); TKEYWORD99 (SD_BOOL); }
"break"                 { count (); return BREAK; }
"case"                  { count (); return CASE; }
"char"                  { count (); return SD_CHAR; }
"__code"                { count (); TKEYWORD (CODE); }
"const"                 { count (); return SD_CONST; }
"continue"              { count (); return CONTINUE; }
"__critical"            { count (); TKEYWORD (CRITICAL); }
"__data"                { count (); TKEYWORD (DATA); }
"default"               { count (); return DEFAULT; }
"do"                    { count (); return DO; }
"double"                { count (); werror (W_DOUBLE_UNSUPPORTED); return SD_FLOAT; }
"else"                  { count (); return ELSE; }
"enum"                  { count (); return ENUM; }
"extern"                { count (); return EXTERN; }
"__far"                 { count (); TKEYWORD (XDATA); }
"__eeprom"              { count (); TKEYWORD (EEPROM); }
"float"                 { count (); return SD_FLOAT; }
"__fixed16x16"          { count (); TKEYWORD (FIXED16X16); }
"__flash"               { count (); TKEYWORD (CODE); }
"for"                   { count (); return FOR; }
"goto"                  { count (); return GOTO; }
"__idata"               { count (); TKEYWORD (IDATA); }
"if"                    { count (); return IF; }
"int"                   { count (); return SD_INT; }
"__interrupt"           { count (); TKEYWORD (INTERRUPT); }
"__nonbanked"           { count (); TKEYWORD (NONBANKED); }
"__banked"              { count (); TKEYWORD (BANKED); }
"__trap"                { count (); TKEYWORD (TRAP); }
"long"                  { count (); return SD_LONG; }
"__near"                { count (); TKEYWORD (DATA); }
"__pdata"               { count (); TKEYWORD (PDATA); }
"__reentrant"           { count (); TKEYWORD (REENTRANT); }
"__shadowregs"          { count (); TKEYWORD (SHADOWREGS); }
"__wparam"              { count (); TKEYWORD (SD_WPARAM); }
"register"              { count (); return REGISTER; }
"return"                { count (); return RETURN; }
"__sfr"                 { count (); TKEYWORD (SFR); }
"__sfr16"               { count (); TKEYWORD (SFR16); }
"__sfr32"               { count (); TKEYWORD (SFR32); }
"__sbit"                { count (); TKEYWORD (SBIT); }
"short"                 { count (); return SD_SHORT; }
"signed"                { count (); return SIGNED; }
"sizeof"                { count (); return SIZEOF; }
"_Alignof"              { count (); return ALIGNOF; }
"__builtin_offsetof"    { count (); return OFFSETOF; }
"__sram"                { count (); TKEYWORD (XDATA); }
"static"                { count (); return STATIC; }
"struct"                { count (); return STRUCT; }
"switch"                { count (); return SWITCH; }
"typedef"               { count (); return TYPEDEF; }
"union"                 { count (); return UNION; }
"unsigned"              { count (); return UNSIGNED; }
"void"                  { count (); return SD_VOID; }
"volatile"              { count (); return VOLATILE; }
"__using"               { count (); TKEYWORD (USING); }
"__naked"               { count (); TKEYWORD (NAKED); }
"while"                 { count (); return WHILE; }
"__xdata"               { count (); TKEYWORD (XDATA); }
"..."                   { count (); return VAR_ARGS; }
"__typeof"              { count (); return TYPEOF; }
"_JavaNative"           { count (); TKEYWORD (JAVANATIVE); }
"__overlay"             { count (); TKEYWORD (OVERLAY); }
"inline"                { count (); TKEYWORD99 (INLINE); }
"_Noreturn"             { count (); return NORETURN;}
"restrict"              { count (); TKEYWORD99 (RESTRICT); }
"__smallc"              { count (); TKEYWORD (SMALLC); }
"__preserves_regs"      { count (); return PRESERVES_REGS; }
"__z88dk_fastcall"      { count (); TKEYWORD (Z88DK_FASTCALL); }
"__z88dk_callee"        { count (); TKEYWORD (Z88DK_CALLEE); }
"__addressmod"          { count (); return ADDRESSMOD; }
"_Static_assert"        { count (); return STATIC_ASSERT; }
"_Alignas"              { count (); return ALIGNAS; }
"_Generic"              { count (); return GENERIC; }
{L}({L}|{D})*           {
  if (!options.dollars_in_ident && strchr (yytext, '$'))
    {
      yyerror ("stray '$' in program");
    }
  count ();
  return check_type();
}
0[bB]{B}+{IS}?          {
  if (!options.std_sdcc)
    {
      yyerror ("binary (0b) constants are not allowed in ISO C");
    }
  count ();
  yylval.val = constIntVal (yytext);
  return CONSTANT;
}
0[xX]{H}+{IS}?               { count (); yylval.val = constIntVal (yytext); return CONSTANT; }
0[0-7]*{IS}?                 { count (); yylval.val = constIntVal (yytext); return CONSTANT; }
[1-9]{D}*{IS}?               { count (); yylval.val = constIntVal (yytext); return CONSTANT; }
{CP}?'(\\.|[^\\'])+'         { count (); yylval.val = charVal (yytext); return CONSTANT; /* ' make syntax highlighter happy */ }
{D}+{E}{FS}?                 { count (); yylval.val = constFloatVal (yytext); return CONSTANT; }
{D}*"."{D}+({E})?{FS}?       { count (); yylval.val = constFloatVal (yytext); return CONSTANT; }
{D}+"."{D}*({E})?{FS}?       { count (); yylval.val = constFloatVal (yytext); return CONSTANT; }
0[xX]{H}+{BE}{FS}?           { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; }
0[xX]{H}*"."{H}+({BE})?{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; }
0[xX]{H}+"."{H}*({BE})?{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; }
\"                           { count (); yylval.yystr = stringLiteral (0); return STRING_LITERAL; }
"L\""                        { count (); if (!options.std_c95) werror(E_WCHAR_STRING_C95); yylval.yystr = stringLiteral ('L'); return STRING_LITERAL; }
"u8\""                       { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral (0); return STRING_LITERAL; }
"u\""                        { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral ('u'); return STRING_LITERAL; }
"U\""                        { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral ('U'); return STRING_LITERAL; }
">>="                   { count (); yylval.yyint = RIGHT_ASSIGN; return RIGHT_ASSIGN; }
"<<="                   { count (); yylval.yyint = LEFT_ASSIGN; return LEFT_ASSIGN; }
"+="                    { count (); yylval.yyint = ADD_ASSIGN; return ADD_ASSIGN; }
"-="                    { count (); yylval.yyint = SUB_ASSIGN; return SUB_ASSIGN; }
"*="                    { count (); yylval.yyint = MUL_ASSIGN; return MUL_ASSIGN; }
"/="                    { count (); yylval.yyint = DIV_ASSIGN; return DIV_ASSIGN; }
"%="                    { count (); yylval.yyint = MOD_ASSIGN; return MOD_ASSIGN; }
"&="                    { count (); yylval.yyint = AND_ASSIGN; return AND_ASSIGN; }
"^="                    { count (); yylval.yyint = XOR_ASSIGN; return XOR_ASSIGN; }
"|="                    { count (); yylval.yyint = OR_ASSIGN; return OR_ASSIGN; }
">>"                    { count (); return RIGHT_OP; }
"<<"                    { count (); return LEFT_OP; }
"++"                    { count (); return INC_OP; }
"--"                    { count (); return DEC_OP; }
"->"                    { count (); return PTR_OP; }
"&&"                    { count (); return AND_OP; }
"||"                    { count (); return OR_OP; }
"<="                    { count (); return LE_OP; }
">="                    { count (); return GE_OP; }
"=="                    { count (); return EQ_OP; }
"!="                    { count (); return NE_OP; }
";"                     { count (); return ';'; }
"{"|"<%"                { count (); ignoreTypedefType = 0; return '{'; }
"}"|"%>"                { count (); return '}'; }
","                     { count (); return ','; }
":"                     { count (); return ':'; }
"="                     { count (); return '='; }
"("                     { count (); ignoreTypedefType = 0; return '('; }
")"                     { count (); return ')'; }
"["|"<:"                { count (); return '['; }
"]"|":>"                { count (); return ']'; }
"."                     { count (); return '.'; }
"&"                     { count (); return '&'; }
"!"                     { count (); return '!'; }
"~"                     { count (); return '~'; }
"-"                     { count (); return '-'; }
"+"                     { count (); return '+'; }
"*"                     { count (); return '*'; }
"/"                     { count (); return '/'; }
"%"                     { count (); return '%'; }
"<"                     { count (); return '<'; }
">"                     { count (); return '>'; }
"^"                     { count (); return '^'; }
"|"                     { count (); return '|'; }
"?"                     { count (); return '?'; }
^{HASH}pragma.*         { count (); process_pragma (yytext); }
^{HASH}.*               { count (); checkCurrFile (yytext); }

"\r\n"                  { count (); }
"\n"                    { count (); }
[ \t\v\f]               { count (); }
\\                      {
  int ch = input ();

  if (ch == '\n')
    count_char (ch);
  else
    {
      /* that could have been removed by the preprocessor anyway */
      werror (W_STRAY_BACKSLASH, column);
      unput (ch);
    }
}
.                       { count (); }
%%

/* flex 2.5.31 undefines yytext_ptr, so we have to define it again */
#ifndef yytext_ptr
#define yytext_ptr yytext
#endif

static void
checkCurrFile (const char *s)
{
  int lNum;
  char *tptr;

  /* skip '#' character */
  if (*s++ != '#')
    return;

  /* get the line number */
  lNum = strtol (s, &tptr, 10);
  if (tptr == s || !isspace ((unsigned char) *tptr))
    return;
  s = tptr;

  /* adjust the line number */
  lineno = lexLineno = lNum - 1;

  /* now see if we have a file name */
  while (*s != '"' && *s)
    ++s;

  if (*s)
    {
      /* there is a file name */
      const char *sb;
      struct dbuf_s dbuf;

      dbuf_init (&dbuf, 128);

      /* skip the double quote */
      sb = ++s;

      /* preprocessor emits escaped file names
       * (e.g. double backslashes on MSDOS-ish file systems),
       * so we have to unescape it */
      while (*s && *s != '"')
        {
          if (*s == '\\')
            {
              /* append chars before backslash */
              dbuf_append (&dbuf, sb, s - sb);
              if (*++s)
                {
                  /* append char after backslash */
                  dbuf_append (&dbuf, s, 1);
                  sb = ++s;
                }
            }
          else
            ++s;
        }
      dbuf_append (&dbuf, sb, s - sb);

      /* DON'T free the file name referenced by lexFilename
       * since it will be dereferenced in the future at least
       * by function printCLine(), see struct iCode members
       * filename in SDCCicode.c */

      filename = lexFilename = dbuf_detach_c_str (&dbuf);
    }
}

static void
count_char (int ch)
{
  switch (ch)
    {
    case '\n':
      column = 0;
      lineno = ++lexLineno;
      break;

    case '\t':
      column += 8 - (column % 8);
      break;

    default:
      ++column;
      break;
    }
}

static void
count (void)
{
  const char *p;

  for (p = yytext; *p; ++p)
    count_char(*p);
}

static int
check_type (void)
{
  symbol *sym = findSym(SymbolTab, NULL, yytext);

  strncpyz(yylval.yychar, yytext, SDCC_NAME_MAX);

  /* check if it is in the table as a typedef */
  if (!ignoreTypedefType && sym && IS_SPEC (sym->etype)
      && SPEC_TYPEDEF (sym->etype) && findSym(TypedefTab, NULL, yytext))
    return (TYPE_NAME);
  /* check if it is a named address space */
  else if (findSym (AddrspaceTab, NULL, yytext))
    return (ADDRSPACE_NAME);
  else
    return(IDENTIFIER);
}

/*
 * Change by JTV 2001-05-19 to not concatenate strings
 * to support ANSI hex and octal escape sequences in string literals
 */

static const char *
stringLiteral (char enc)
{
#define STR_BUF_CHUNCK_LEN  1024
  int ch;
  static struct dbuf_s dbuf;  /* reusable string literal buffer */

  if (dbuf.alloc == 0)
    dbuf_init(&dbuf, STR_BUF_CHUNCK_LEN);
  else
    dbuf_set_length(&dbuf, 0);

  switch (enc)
    {
    case 'u': // UTF-16
      dbuf_append_str(&dbuf, "u\"");
      break;
    case 'L':
    case 'U': // UTF-32
      enc = 'U';
      dbuf_append_str(&dbuf, "U\"");
      break;
    default: // UTF-8 or whatever else the source character set is encoded in
      dbuf_append_char(&dbuf, '"');
    }

  /* put into the buffer till we hit the first \" */

  for (; ; )
    {
      ch = input();
      count_char(ch);
      if (ch == EOF)
        break;

      switch (ch)
        {
        case '\\':
          /* if it is a \ then escape char's are allowed */
          ch = input();
          count_char(ch);
          if (ch == '\n')
            {
              /* \<newline> is a continuator */
            }
          else
            {
              char buf[2];

              if (ch == EOF)
                goto out;

              buf[0] = '\\';
              buf[1] = ch;
              dbuf_append(&dbuf, buf, 2); /* get the escape char, no further check */
            }
          break; /* carry on */

        case '\n':
          /* if new line we have a new line break, which is illegal */
          werror(W_NEWLINE_IN_STRING);
          dbuf_append_char(&dbuf, '\n');
          break;

        case '"':
          /* if this is a quote then we have work to do */
          /* find the next non whitespace character     */
          /* if that is a double quote then carry on    */
          dbuf_append_char(&dbuf, '"');  /* Pass end of this string or substring to evaluator */
          while ((ch = input()) && (isspace(ch) || ch == '\\' || ch == '#'))
            {
              switch (ch)
                {
                case '\\':
                  count_char(ch);
                  if ((ch = input()) != '\n')
                    {
                      werror(W_STRAY_BACKSLASH, column);
                      if (ch != EOF)
                        unput(ch);
                      else
                        count_char(ch);
                    }
                  else
                    count_char(ch);
                  break;

                case '\n':
                  count_char(ch);
                  break;

                case '#':
                  if (column == 0)
                    {
                      /* # at the beginning of the line: collect the entire line */
                      struct dbuf_s linebuf;
                      const char *line;

                      count_char(ch);

                      dbuf_init(&linebuf, STR_BUF_CHUNCK_LEN);
                      dbuf_append_char(&linebuf, '#');

                      while ((ch = input()) != EOF && ch != '\n')
                        dbuf_append_char(&linebuf, (char)ch);

                      if (ch == '\n')
                        count_char(ch);

                      line = dbuf_c_str(&linebuf);

                      /* process the line */
                      if (startsWith(line, "#pragma"))
                        process_pragma(line);
                      else
                        checkCurrFile(line);

                      dbuf_destroy(&linebuf);
                    }
                  else
                    {
                      unput(ch);
                      goto out;
                    }

                default:
                  count_char(ch);
                  break;
                }
            }

          if (ch == EOF)
            goto out;

          if (ch == 'u' || ch == 'U' || ch == 'L') /* Could be an utf-16 or utf-32 wide string literal prefix */
            {
              int ch2;

              if (!(options.std_c11 || options.std_c95 && ch == 'L'))
                {
                  werror (ch == 'L' ? E_WCHAR_STRING_C95 : E_WCHAR_STRING_C11);
                  unput(ch);
                  goto out;
                }

              ch2 = input();
              if (ch2 != '"')
                unput (ch2);
              else /* It is an utf-16 or utf-32 wide string literal prefix */
                {
                  if (!enc) 
                    {
                      dbuf_prepend_char(&dbuf, ch == 'L' ? 'U' : ch);
                      enc = ch;
                    }
                  count_char(ch);
                  count_char(ch2);
                  break;
                }
            }

          if (ch == 'u') /* Could be an utf-8 wide string literal prefix */
            {
              ch = input();
              if (ch != '8')
                {
                  unput(ch);
                  unput('u');
                  goto out;
                }
              ch = input();
              if (ch != '"')
                {
                  unput(ch);
                  unput('8');
                  unput('u');
                  goto out;
                }
            }
          if (ch != '"')
            {
              unput(ch);
              goto out;
            }
          count_char(ch);
          break;

        default:
          dbuf_append_char(&dbuf, (char)ch);  /* Put next substring introducer into output string */
        }
    }

out:
  return dbuf_c_str(&dbuf);
}


enum {
   P_SAVE = 1,
   P_RESTORE,
   P_INDUCTION,
   P_NOINDUCTION,
   P_NOINVARIANT,
   P_STACKAUTO,
   P_OVERLAY_,     /* I had a strange conflict with P_OVERLAY while */
                   /* cross-compiling for MINGW32 with gcc 3.2 */
   P_NOOVERLAY,
   P_LESSPEDANTIC,
   P_NOGCSE,
   P_CALLEE_SAVES,
   P_EXCLUDE,
   P_NOIV,
   P_LOOPREV,
   P_DISABLEWARN,
   P_OPTCODESPEED,
   P_OPTCODESIZE,
   P_OPTCODEBALANCED,
   P_STD_C89,
   P_STD_C99,
   P_STD_C11,
   P_STD_C2X,
   P_STD_SDCC89,
   P_STD_SDCC99,
   P_CODESEG,
   P_CONSTSEG
};


/* SAVE/RESTORE stack */
#define SAVE_RESTORE_SIZE 128

STACK_DCL(options_stack, struct options *, SAVE_RESTORE_SIZE)
STACK_DCL(optimize_stack, struct optimize *, SAVE_RESTORE_SIZE)
STACK_DCL(SDCCERRG_stack, struct SDCCERRG *, SAVE_RESTORE_SIZE)

/*
 * cloneXxx functions should be updated every time a new set is
 * added to the options or optimize structure!
 */

static struct options *
cloneOptions (struct options *opt)
{
  struct options *new_opt;

  new_opt = Safe_malloc(sizeof (struct options));

  /* clone scalar values */
  *new_opt = *opt;

  /* clone sets */
  new_opt->calleeSavesSet = setFromSetNonRev(opt->calleeSavesSet);
  new_opt->excludeRegsSet = setFromSetNonRev(opt->excludeRegsSet);
  /* not implemented yet: */
  /* new_opt->olaysSet = setFromSetNonRev(opt->olaysSet); */

  return new_opt;
}

static struct optimize *
cloneOptimize (struct optimize *opt)
{
  struct optimize *new_opt;

  new_opt = Safe_malloc(sizeof (struct optimize));

  /* clone scalar values */
  *new_opt = *opt;

  return new_opt;
}

static struct SDCCERRG *
cloneSDCCERRG (struct SDCCERRG *val)
{
  struct SDCCERRG *new_val;

  new_val = Safe_malloc(sizeof (struct SDCCERRG));

  /* clone scalar values */
  *new_val = *val;

  return new_val;
}

static void
copyAndFreeOptions (struct options *dest, struct options *src)
{
  /* delete dest sets */
  deleteSet(&dest->calleeSavesSet);
  deleteSet(&dest->excludeRegsSet);
  /* not implemented yet: */
  /* deleteSet(&dest->olaysSet); */

  /* copy src to dest */
  *dest = *src;

  Safe_free(src);
}

static void
copyAndFreeOptimize (struct optimize *dest, struct optimize *src)
{
  /* copy src to dest */
  *dest = *src;

  Safe_free(src);
}

static void
copyAndFreeSDCCERRG (struct SDCCERRG *dest, struct SDCCERRG *src)
{
  /* copy src to dest */
  *dest = *src;

  Safe_free(src);
}

/*
 * returns 1 if the pragma was processed, 0 if not
 */
static int
doPragma (int id, const char *name, const char *cp)
{
  struct pragma_token_s token;
  int err = 0;
  int processed = 1;

  init_pragma_token(&token);

  switch (id)
    {
    case P_SAVE:
      {
        cp = get_pragma_token(cp, &token);
        if (TOKEN_EOL != token.type)
          {
            err = 1;
            break;
          }

        STACK_PUSH(options_stack, cloneOptions(&options));
        STACK_PUSH(optimize_stack, cloneOptimize(&optimize));
        STACK_PUSH(SDCCERRG_stack, cloneSDCCERRG(&_SDCCERRG));
      }
      break;

    case P_RESTORE:
      {
        struct options *optionsp;
        struct optimize *optimizep;
        struct SDCCERRG *sdccerrgp;

        cp = get_pragma_token(cp, &token);
        if (TOKEN_EOL != token.type)
          {
            err = 1;
            break;
          }

        optionsp = STACK_POP(options_stack);
        copyAndFreeOptions(&options, optionsp);

        optimizep = STACK_POP(optimize_stack);
        copyAndFreeOptimize(&optimize, optimizep);

        sdccerrgp = STACK_POP(SDCCERRG_stack);
        copyAndFreeSDCCERRG(&_SDCCERRG, sdccerrgp);
      }
      break;

    case P_INDUCTION:
    case P_NOINDUCTION:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      optimize.loopInduction = (id == P_INDUCTION) ? 1 : 0;
      break;

    case P_NOINVARIANT:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      optimize.loopInvariant = 0;
      break;

    case P_STACKAUTO:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.stackAuto = 1;
      break;

    case P_NOGCSE:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      optimize.global_cse = 0;
      break;

    case P_OVERLAY_:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      break; /* notyet */

    case P_NOOVERLAY:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.noOverlay = 1;
      break;

    case P_LESSPEDANTIC:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.lessPedantic = 1;
      setErrorLogLevel(ERROR_LEVEL_WARNING);
      break;

    case P_CALLEE_SAVES:
      /* append to the functions already listed
         in callee-saves */
      setParseWithComma(&options.calleeSavesSet, cp);
      err = -1;
      break;

    case P_EXCLUDE:
      {
        deleteSet(&options.excludeRegsSet);
        setParseWithComma(&options.excludeRegsSet, cp);
        err = -1;
      }
      break;

    case P_NOIV:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.noiv = 1;
      break;

    case P_LOOPREV:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      optimize.noLoopReverse = 1;
      break;

    case P_DISABLEWARN:
      {
        int warn;

        cp = get_pragma_token(cp, &token);
        if (TOKEN_INT != token.type)
          {
            err = 1;
            break;
          }
        warn = token.val.int_val;

        cp = get_pragma_token(cp, &token);
        if (TOKEN_EOL != token.type)
          {
            err = 1;
            break;
          }

        setWarningDisabled(warn);
      }
      break;

    case P_OPTCODESPEED:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      optimize.codeSpeed = 1;
      optimize.codeSize = 0;
      break;

    case P_OPTCODESIZE:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      optimize.codeSpeed = 0;
      optimize.codeSize = 1;
      break;

    case P_OPTCODEBALANCED:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      optimize.codeSpeed = 0;
      optimize.codeSize = 0;
      break;

    case P_STD_C89:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.std_c99 = 0;
      options.std_c11 = 0;
      options.std_c2x = 0;
      options.std_sdcc = 0;
      break;

    case P_STD_C99:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.std_c99 = 1;
      options.std_c11 = 0;
      options.std_c2x = 0;
      options.std_sdcc = 0;
      break;

    case P_STD_C11:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.std_c99 = 1;
      options.std_c11 = 1;
      options.std_c2x = 0;
      options.std_sdcc = 0;
      break;

    case P_STD_C2X:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.std_c99 = 1;
      options.std_c11 = 1;
      options.std_c2x = 1;
      options.std_sdcc = 0;
      break;

    case P_STD_SDCC89:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.std_c99 = 0;
      options.std_c11 = 0;
      options.std_c2x = 0;
      options.std_sdcc = 1;
      break;

    case P_STD_SDCC99:
      cp = get_pragma_token(cp, &token);
      if (TOKEN_EOL != token.type)
        {
          err = 1;
          break;
        }

      options.std_c99 = 1;
      options.std_c11 = 0;
      options.std_c2x = 0;
      options.std_sdcc = 1;
      break;

    case P_CODESEG:
    case P_CONSTSEG:
      {
        struct dbuf_s segname;

        cp = get_pragma_token(cp, &token);
        if (token.type == TOKEN_EOL)
          {
            err = 1;
            break;
          }

        dbuf_init(&segname, 16);
        dbuf_printf(&segname, "%-8s(CODE)", get_pragma_string(&token));

        cp = get_pragma_token(cp, &token);
        if (token.type != TOKEN_EOL)
          {
            dbuf_destroy(&segname);
            err = 1;
            break;
          }

        if (id == P_CODESEG)
          options.code_seg = dbuf_detach(&segname);
        else
          options.const_seg = dbuf_detach(&segname);
      }
      break;

    default:
      processed = 0;
      break;
    }

  get_pragma_token(cp, &token);

  if (1 == err || (0 == err && token.type != TOKEN_EOL))
    werror(W_BAD_PRAGMA_ARGUMENTS, name);

  free_pragma_token(&token);
  return processed;
}

static struct pragma_s pragma_tbl[] = {
  { "save",              P_SAVE,            0, doPragma },
  { "restore",           P_RESTORE,         0, doPragma },
  { "induction",         P_INDUCTION,       0, doPragma },
  { "noinduction",       P_NOINDUCTION,     0, doPragma },
  { "noinvariant",       P_NOINVARIANT,     0, doPragma },
  { "noloopreverse",     P_LOOPREV,         0, doPragma },
  { "stackauto",         P_STACKAUTO,       0, doPragma },
  { "nogcse",            P_NOGCSE,          0, doPragma },
  { "overlay",           P_OVERLAY_,        0, doPragma },
  { "nooverlay",         P_NOOVERLAY,       0, doPragma },
  { "callee_saves",      P_CALLEE_SAVES,    0, doPragma },
  { "exclude",           P_EXCLUDE,         0, doPragma },
  { "noiv",              P_NOIV,            0, doPragma },
  { "less_pedantic",     P_LESSPEDANTIC,    0, doPragma },
  { "disable_warning",   P_DISABLEWARN,     0, doPragma },
  { "opt_code_speed",    P_OPTCODESPEED,    0, doPragma },
  { "opt_code_size",     P_OPTCODESIZE,     0, doPragma },
  { "opt_code_balanced", P_OPTCODEBALANCED, 0, doPragma },
  { "std_c89",           P_STD_C89,         0, doPragma },
  { "std_c99",           P_STD_C99,         0, doPragma },
  { "std_c11",           P_STD_C11,         0, doPragma },
  { "std_c2x",           P_STD_C2X,         0, doPragma },
  { "std_sdcc89",        P_STD_SDCC89,      0, doPragma },
  { "std_sdcc99",        P_STD_SDCC99,      0, doPragma },
  { "codeseg",           P_CODESEG,         0, doPragma },
  { "constseg",          P_CONSTSEG,        0, doPragma },
  { NULL,                0,                 0, NULL },
};

/*
 * returns 1 if the pragma was processed, 0 if not
 */
int
process_pragma_tbl (const struct pragma_s *pragma_tbl, const char *s)
{
  struct pragma_token_s token;
  int i;
  int ret = 0;

  init_pragma_token(&token);

  s = get_pragma_token(s, &token);

  /* skip separating whitespace */
  while ('\n' != *s && isspace((unsigned char)*s))
    s++;

  for (i = 0; NULL != pragma_tbl[i].name; ++i)
    {
      /* now compare and do what needs to be done */
      if (strcmp(get_pragma_string(&token), pragma_tbl[i].name) == 0)
        {
          if (pragma_tbl[i].deprecated != 0)
            werror(W_DEPRECATED_PRAGMA, pragma_tbl[i].name);

          ret = (*pragma_tbl[i].func)(pragma_tbl[i].id, pragma_tbl[i].name, s);
          break;
        }
    }

  free_pragma_token(&token);
  return ret;
}

static int
process_pragma (const char *s)
{
  struct pragma_token_s token;

  init_pragma_token(&token);

  s = get_pragma_token(s, &token);
  if (0 != strcmp("#pragma", get_pragma_string(&token)))
    {
      /* Oops, something went totally wrong - internal error */
      wassertl(0, "pragma parser internal error");
    }

  /* skip spaces */
  while ('\n' != *s && isspace((unsigned char)*s))
    ++s;

  /* First give the port a chance */
  if (port->process_pragma && port->process_pragma(s))
    return 1;

  if (process_pragma_tbl(pragma_tbl, s))
    {
      return 1;
    }
  else
    {
      werror(W_UNKNOWN_PRAGMA, s);
      return 0;
    }
}

int
yywrap (void)
{
  if (!STACK_EMPTY(options_stack) || !STACK_EMPTY(optimize_stack))
    werror(W_SAVE_RESTORE);

  return 1;
}

int
yyerror (char *s)
{
  fflush(stdout);

  werror (S_SYNTAX_ERROR, yytext, column);

  return 0;
}
