/* -*- c++ -*- Scanner for the Maria Debugger */

%option nostdinit
%option stack
%option prefix="de"
%option 8bit
%option caseful
%option warn
%option pointer
%option never-interactive
%option nodefault
%option noinput
%option nounput
%option noyywrap
%option noyy_top_state
%option noyy_scan_buffer
%option noyy_scan_bytes
%option noyy_scan_string

%{
# include <stdio.h>
# include <limits.h>
# include <string.h>
# include <assert.h>

# include "typedefs.h"
# include "marde.tab.h"

# include "StringBuffer.h"
# include "util.h"
# include "Printer.h"

  /** @file marde.lex
   * Lexical analyser for the query language
   */

  /* Copyright © 1999-2003 Marko Mäkelä (msmakela@tcs.hut.fi).

  This file is part of MARIA, a reachability analyzer and model checker
  for high-level Petri nets.

  MARIA 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.

  MARIA 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.

  The GNU General Public License is often shipped with GNU software, and
  is generally kept in a file called COPYING or LICENSE.  If you do not
  have a copy of the license, write to the Free Software Foundation,
  59 Temple Place, Suite 330, Boston, MA 02111 USA. */

  /** The printer object */
  extern class Printer thePrinter;
# include <list>
# include <stack>
  /** Stack of include files */
  struct input
  {
    /** name of the input file */
    class StringBuffer* name;
    /** input line number */
    unsigned lineno;
    /** number of unterminated preprocessor #if statements */
    unsigned condlevel;
    /** buffer state */
    YY_BUFFER_STATE bs;
  };
  static std::stack<struct input, std::list<struct input> > in;
  /** Current preprocessor condition directive */
  static enum { none, ifdef, ifndef, define, undef } currentDirective;
  /** preprocessor #if nesting level */
  static unsigned condlevel = 0;
  /** deepest #if nesting level where #else has an effect */
  static unsigned passlevel = 0;
  /** flag: pass tokens to the parser (instead of eating them up) */
  static bool pass = true;
  /** conditionally ignore the input */
# define I if (assert (!pass || condlevel == passlevel), !pass) break

  /** Dynamically growing string buffer */
  static class StringBuffer sbuf;
  /** @name String handling macros.  All of these use sbuf. */
  /*@{*/
# define CREAT memcpy (sbuf.create (yyleng), yytext, yyleng + 1)
# define INITSTR sbuf.create (0)
# define EXTEND do {						\
    unsigned len = sbuf.getLength ();				\
    memcpy (sbuf.extend (yyleng) + len, yytext, yyleng + 1);	\
  } while (0)
# define APPEND(c) do {				\
    unsigned len = sbuf.getLength ();		\
    (&((sbuf.extend (1))[len] = c))[1] = 0;	\
  } while (0)
# define COPY							\
  (strlen (delval.s = sbuf.copy ()) == sbuf.getLength ()) ||	\
  (deerror ("truncating name containing NUL chars"), 0)
  /*@}*/

  /** @name Start condition stack handling */
  /*@{*/
# define SC_FLUSH	yy_start_stack_ptr = 0, BEGIN (INITIAL)
# define SC_POP		yy_pop_state ()
# define SC_POP_B	do { SC_POP; yyless (0); } while (0)
# define SC_PUSH(s)	yy_push_state (s)
# define SC_SWITCH(s)	BEGIN (s)
# define SC_SWITCH_B(s)	do { yyless (0); SC_SWITCH (s); } while (0)
# define SC_CALL(s,r)	do { rt = r; SC_PUSH (ret); SC_PUSH (s); } while (0)
# define SC_USE(s,r)	do { SC_SWITCH (r); SC_PUSH (s); } while (0)
  /*@}*/

  /** initial source file name */
  extern const char* scriptname;
  /** Name of Petri Nets parsed from the command line */
  extern const char* scriptcommand;
  /** Petri Net model to be parsed from the command line */
  extern const char* scriptstring;
  /** Cursor to scriptstring */
  static const char* stringptr = NULL;

  /** flag: expecting a command keyword */
  extern bool command;

  /** Return a token
   * @param token	token to be returned
   */
# define RETURN(token)	return command = false, token
  /** Return a token as a command or as an identifier
   * @param token	token to be returned
   */
# define RETURNB(token)	return command ? token : (CREAT, COPY, NAME)
  /** Return a command or an identifier token, and clear the "command flag"
   * @param token	token to be returned
   */
# define RETURNC(token)							\
  return command ? (command = false, token) : (CREAT, COPY, NAME)
  /** source file line number */
  static unsigned lineno = 1;
  /** source file name */
  static class StringBuffer filename;

  /** Token to be returned to the parser */
  static int rt;
  /** Flag: input constant overflow */
  static bool overflow = false;
  /** Number of digits read in an octal or hexadecimal character constant */
  static unsigned digits = 0;
  /** Character constant being parsed in a string */
  static char character;

  /** Reset the lexical analyzer */
  void delexreset (void);

  /** warning flag (true=print warnings) */
  bool dewarnings = true;

  /**
   * display a message prefix at beginning of line
   * @return		number of characters written
   */
  unsigned demsg (void);

  /**
   * generic warning reporting function
   * @param s		warning message
   */
  void dewarn (const char* s);

  /**
   * generic parser error reporting function
   * @param s		error message
   */
  void deerror (const char* s);

  /** Open a script
   * @param name	name of the script
   * @return		a descriptor to the file, or NULL on error
   */
  extern FILE* openscript (const char* name);
  /** Switch files on EOF (return to the file that included the current file)
   * @return	true if there was a parent file
   */
  static bool popInclude (void);

  /** Check whether a preprocessor symbol is defined
   * @param sym		name of the symbol
   * @return		whether the symbol is defined
   */
  extern bool defined (const char* sym);
  /** Define a preprocessor symbol
   * @param sym		name of the symbol
   */
  extern void define_sym (const char* sym);
  /** Undefine a preprocessor symbol
   * @param sym		name of the symbol
   */
  extern void undef_sym (const char* sym);

  /** Advance to next buffer */
# define NEXTBUF do if (!popInclude ()) { SC_FLUSH; yyterminate (); } while (0)
  /** User-defined lexical analyser initialisation */
# define YY_USER_INIT NEXTBUF

  /** Read more input */
# define YY_INPUT(buf,result,max_size)					\
  if (!dein || dein == stdin) {						\
    if (!stringptr)							\
      result = 0;							\
    else if (*stringptr) {						\
      if ((result = strlen (stringptr)) > max_size)			\
	result = max_size;						\
      memcpy (buf, stringptr, result);					\
      stringptr += result;						\
    }									\
    else if (max_size >= 1)						\
      *buf = '\n', stringptr = NULL, result = 1;			\
    else								\
      result = 0;							\
  }									\
  else if (!(result = fread (buf, 1, max_size, dein)) && ferror (dein))	\
    deerror ("input error"), delexreset ()
%}

/* Start conditions */
/* returning to the parser */
%x ret
/* inside a comment */
%x comment

/* parsing a preprocessor directive */
%x pr
/* parsing a preprocessor directive, ignoring the rest of the line */
%x pr_ignore
/* parsing a preprocessor directive "line" */
%x pr_line
/* parsing a preprocessor directive "line", got line number */
%x pr_line_number
/* parsing a preprocessor directive "line", got file name */
%x pr_line_name
/* parsing a preprocessor directive "include" */
%x pr_incl
/* parsing a preprocessor directive "include", got file name */
%x pr_incl_name
/* parsing a preprocessor condition name */
%x pr_cond

/* parsing an octal constant */
%x octal
/* parsing a hexadecimal constant */
%x hexadecimal
/* parsing a decimal constant */
%x decimal

/* parsing a name */
%x dename
/* parsing a character constant */
%x dechar
/* parsing an octal character constant */
%x dechar_oct
/* parsing a hexadecimal character constant */
%x dechar_hex
/* parsing a string constant */
%x destring
/* parsing an octal character constant in a string */
%x destring_oct
/* parsing a hexadecimal character constant in a string */
%x destring_hex

ws		[ \t\r\v\f]+
nn		[^\\A-Za-z0-9_]

%%

<ret>.|\n	{ SC_POP_B; I; if (rt == NAME) COPY; RETURN (rt); }
<ret><<EOF>>	{ SC_POP; NEXTBUF; I; if (rt == NAME) COPY; RETURN (rt); }

<comment>{
  [^*\n]*
  [^*\n]*\n		lineno++;
  "*"+[^*/\n]*
  "*"+[^*/\n]*\n	lineno++;
  "*"+"/"		SC_POP;
  <<EOF>>		{ deerror ("EOF in comment"); SC_POP; NEXTBUF; }
}

<pr,pr_line,pr_line_number,pr_line_name,pr_incl,pr_incl_name,pr_cond>{
  {ws}+
  "//"[^\n]*
  "/*"		SC_PUSH (comment);
  <<EOF>>	{ SC_POP; NEXTBUF; }
}

<pr>{
  "include"	SC_SWITCH (pr_incl);
  "line"	SC_SWITCH (pr_line);
  "else"	{
    if (!condlevel)
      deerror ("#else without #if");
    else if (pass)
      assert (passlevel == condlevel), pass = false, passlevel--;
    else if (passlevel + 1 == condlevel)
      pass = true, passlevel++;
    else
      assert (passlevel < condlevel);
    currentDirective = none; SC_SWITCH (pr_cond);
  }
  "endif"	{
    if (!condlevel)
      deerror ("#endif without #if");
    else if (passlevel >= --condlevel)
      pass = true, passlevel = condlevel;
    currentDirective = none; SC_SWITCH (pr_cond);
  }

  "ifdef"	{ currentDirective = ifdef; SC_SWITCH (pr_cond); }
  "ifndef"	{ currentDirective = ifndef; SC_SWITCH (pr_cond); }
  "define"	{ currentDirective = define; SC_SWITCH (pr_cond); }
  "undef"	{ currentDirective = undef; SC_SWITCH (pr_cond); }

  [A-Za-z_][A-Za-z0-9_]* {
    deerror ("unknown preprocessor directive");
    SC_SWITCH (pr_ignore);
  }
  !			{
    SC_SWITCH (pr_ignore); /* #! interpreter command */
  }
  .			{
    deerror ("error in preprocessor directive"); SC_SWITCH (pr_ignore);
  }
  \n			{
    deerror ("expected a preprocessor directive"); SC_POP; lineno++;
  }
}

<pr_ignore>{
  [^\n]+		SC_POP;
  \n			{ SC_POP; lineno++; }
  <<EOF>>		{ SC_POP; NEXTBUF; }
}

<pr_line>{
  0x		{
    delval.i = 0, overflow = false;
    SC_USE (hexadecimal, pr_line_number);
  }
  0		{
    delval.i = 0, overflow = false;
    SC_USE (octal, pr_line_number);
  }
  [1-9]		{
    delval.i = *yytext - '0', overflow = false;
    SC_USE (decimal, pr_line_number);
  }
  \"			{ INITSTR; SC_USE (destring, pr_line_name); }
  \n			{ lineno++; SC_POP; }
  .			{
    deerror ("error in preprocessor directive #line");
    SC_SWITCH (pr_ignore);
  }
}

<pr_line_number>.|\n	{
  SC_SWITCH_B (pr_line);
  lineno = delval.i - 1;
}

<pr_line_name>.|\n	{ SC_SWITCH_B (pr_line); filename = sbuf; }

<pr_incl>{
  \"			{ INITSTR; SC_USE (destring, pr_incl_name); }
  \n			{
    deerror ("unexceptedly terminated preprocessor directive #include");
    lineno++; SC_POP;
  }
  .			{
    deerror ("error in preprocessor directive #include");
    SC_SWITCH (pr_ignore);
  }
}

<pr_incl_name>.|\n	{
  SC_POP_B; I;
  if (FILE* f = openscript (sbuf.getString ())) {
    struct input is = {
      new class StringBuffer (filename), lineno, condlevel, YY_CURRENT_BUFFER
    };
    in.push (is);
    filename = sbuf;
    lineno = 1;
    yy_switch_to_buffer (yy_create_buffer (f, YY_BUF_SIZE));
  }
  else {
    perror (sbuf.getString ());
    deerror ("could not open include file");
  }
}

<pr_cond>{
  [A-Za-z_][A-Za-z0-9_]*	{
    switch (currentDirective) {
    case none:
      deerror ("error in preprocessor directive"); SC_SWITCH (pr_ignore);
      break;
    case ifdef:
      condlevel++;
      if (pass) {
	if (!defined (yytext))
	  pass = false;
	else
	  passlevel = condlevel;
      }
      break;
    case ifndef:
      condlevel++;
      if (pass) {
	if (defined (yytext))
	  pass = false;
	else
	  passlevel = condlevel;
      }
      break;
    case define:
      if (pass) define_sym (yytext);
      break;
    case undef:
      if (pass) undef_sym (yytext);
      break;
    }
    currentDirective = none;
  }

  \n	{
    if (currentDirective != none)
      deerror ("missing condition name");
    lineno++; SC_POP;
  }

  .				{
    deerror ("error in preprocessor condition directive");
    SC_SWITCH (pr_ignore);
  }
}

{ws}+
\n		lineno++;
"/*"		SC_PUSH (comment);
"//"[^\n]*
^#		SC_PUSH (pr);

"union"/{nn}		{ I; RETURN (UNION); }
"is"/{nn}		{ I; RETURN (IS); }
"until"/{nn}		{ I; RETURN (UNTIL); }
"release"/{nn}		{ I; RETURN (RELEASE); }
"subset"/{nn}		{ I; RETURN (SUBSET); }
"intersect"/{nn}	{ I; RETURN (INTERSECT); }
"equals"/{nn}		{ I; RETURN (EQUALS); }
"minus"/{nn}		{ I; RETURN (MINUS); }
"atom"/{nn}		{ I; RETURN (ATOM_); }
"empty"/{nn}		{ I; RETURN (EMPTY); }
"map"/{nn}		{ I; RETURN (MAP); }

"place"/{nn}		{ I; RETURN (PLACE); }
"trans"/{nn}		{ I; RETURN (TRANS); }
"in"/{nn}		{ I; RETURN (IN_); }
"out"/{nn}		{ I; RETURN (OUT_); }
"gate"/{nn}		{ I; RETURN (GATE); }
"true"/{nn}		{ I; RETURN (TRUE_); }
"false"/{nn}		{ I; RETURN (FALSE_); }
"undefined"/{nn}	{ I; RETURN (UNDEFINED); }
"fatal"/{nn}		{ I; RETURN (FATAL); }

"cardinality"/{nn}	{ I; RETURN (CARDINALITY); }
"min"/{nn}		{ I; RETURN (MIN_); }
"max"/{nn}		{ I; RETURN (MAX_); }

"=>"		{ I; RETURN (IMPL); }
"^^"		{ I; RETURN (XOR); }
"<=>"		{ I; RETURN (EQUIV); }
"||"		{ I; RETURN (OR); }
"&&"		{ I; RETURN (AND); }
"<>"		{ I; RETURN (FINALLY); }
"[]"		{ I; RETURN (GLOBALLY); }
"()"		{ I; RETURN (NEXT); }

"=="		{ I; RETURN (EQ); }
"!="		{ I; RETURN (NE); }
">="		{ I; RETURN (GE); }
"<="		{ I; RETURN (LE); }
"<<"		{ I; RETURN (ROL); }
">>"		{ I; RETURN (ROR); }

"model"/{nn}	{ I; RETURNC (MODEL); }
"graph"/{nn}	{ I; RETURNC (GRAPH); }
"lsts"/{nn}	{ I; RETURNC (LSTS); }
"unfold"/{nn}	{ I; RETURNC (UNFOLD); }
"breadth"/{nn}	{ I; RETURNC (BREADTH); }
"depth"/{nn}	{ I; RETURNC (DEPTH); }
"strong"/{nn}	{ I; RETURNC (STRONG); }
"path"/{nn}	{ I; RETURNC (PATH); }
"terminal"/{nn}	{ I; RETURNC (TERMINAL); }
"components"/{nn}	{ I; RETURNC (COMPONENTS); }
"cd"/{nn}	{ I; RETURNC (CD); }
"translator"/{nn}	{ I; RETURNC (TRANSLATOR); }
"compiledir"/{nn}	{ I; RETURNC (COMPILEDIR); }
"visual"/{nn}	{ I; RETURNB (VISUAL); }
"dump"/{nn}	{ I; RETURNC (DUMP); }
"dumpgraph"/{nn}	{ I; RETURNC (DUMPGRAPH); }
"eval"/{nn}	{ I; RETURNC (EVAL); }
"hide"/{nn}	{ I; RETURNC (HIDE); }
"subnet"/{nn}	{ I; RETURNC (SUBNET); }
"show"/{nn}	{ I; RETURNC (SHOW); }
"succ"/{nn}	{ I; RETURNC (SUCC); }
"pred"/{nn}	{ I; RETURNC (PRED); }
"go"/{nn}	{ I; RETURNC (GO); }
"stats"/{nn}	{ I; RETURNC (STATS); }
"time"/{nn}	{ I; RETURNC (TIME); }
"help"/{nn}	{ I; RETURNC (HELP); }
"log"/{nn}	{ I; RETURNC (LOG); }
"function"/{nn}	{ I; RETURNC (FUNCTION); }
"prompt"/{nn}	{ I; RETURNC (PROMPT); }
"exit"/{nn}	{ I; RETURNC (EXIT); }

@?@?0x		{
  delval.i = 0, overflow = false;
  SC_CALL (hexadecimal,
	   *yytext == '@' ? (yytext[1] == '@' ? COMP : STATE) : NUMBER);
}
@?@?0		{
  delval.i = 0, overflow = false;
  SC_CALL (octal,
	   *yytext == '@' ? (yytext[1] == '@' ? COMP : STATE) : NUMBER);
}
[1-9]		{
  delval.i = *yytext - '0', overflow = false; SC_CALL (decimal, NUMBER);
}
@[1-9]		{
  delval.i = yytext[1] - '0', overflow = false; SC_CALL (decimal, STATE);
}
@@[1-9]		{
  delval.i = yytext[2] - '0', overflow = false; SC_CALL (decimal, COMP);
}

<hexadecimal>{
  [0-9]		{
    if (overflow);
    else if (delval.i > CARD_T_MAX >> 4)
      deerror ("hexadecimal constant too large"), overflow = true;
    else
      (delval.i <<= 4) |= *yytext - '0';
  }
  [A-F]		{
    if (overflow);
    else if (delval.i > CARD_T_MAX >> 4)
      deerror ("hexadecimal constant too large"), overflow = true;
    else
      (delval.i <<= 4) |= *yytext - 'A' + 0x0A;
  }
  [a-f]		{
    if (overflow);
    else if (delval.i > CARD_T_MAX >> 4)
      deerror ("hexadecimal constant too large"), overflow = true;
    else
      (delval.i <<= 4) |= *yytext - 'a' + 0x0A;
  }
}

<octal>[0-7]	{
  if (overflow);
  else if (delval.i > CARD_T_MAX >> 3)
    deerror ("octal constant too large"), overflow = true;
  else
    (delval.i <<= 3) |= *yytext - '0';
}

<decimal>[0-9]	{
  if (!overflow) {
    card_t v = delval.i * 10 + (*yytext - '0');
    if (v / 10 != delval.i)
      deerror ("decimal constant too large"), overflow = true;
    else
      delval.i = v;
  }
}

<hexadecimal,octal,decimal>{
  [0-9a-zA-Z_]	{
    deerror ("improperly terminated numeric constant");
    yyless (0); SC_POP;
  }
  .|\n		{
    yyless (0); SC_POP;
  }
  <<EOF>>	{
    SC_POP; NEXTBUF;
  }
}

[A-Za-z_][A-Za-z0-9_]*	{ CREAT; SC_PUSH (dename); }
\\.			{ INITSTR; APPEND (yytext[1]); SC_PUSH (dename); }

<dename>{
  \\\n{ws}*		lineno++;
  \\.			APPEND (yytext[1]);
  [A-Za-z0-9_]+		EXTEND;
  .|\n			{ SC_POP_B; I; COPY; RETURN (NAME); }
}

"'"			{ overflow = false; SC_PUSH (dechar); }

<dechar>{
  \'			{
    if (!overflow) {
      deerror ("null char constant");
      delval.c = 0;
    }
    SC_POP; I; RETURN (CHARACTER);
  }
  \\\n{ws}*		lineno++;
  \\/[0-7]		{
    if (!overflow) {
      delval.c = 0, digits = 0, overflow = true; SC_SWITCH (dechar_oct);
    }
    else
      deerror ("char constant too large, ignoring `\\'");
  }
  \\x/[0-9a-fA-F]	{
    if (!overflow) {
      delval.c = 0, digits = 0, overflow = true; SC_SWITCH (dechar_hex);
    }
    else
      deerror ("char constant too large, ignoring `\\x'");
  }
  \\.			{
    if (!overflow) {
      overflow = true;
      switch (yytext[1]) {
      case 'a': delval.c = '\a'; break;
      case 'b': delval.c = '\b'; break;
      case 't': delval.c = '\t'; break;
      case 'n': delval.c = '\n'; break;
      case 'v': delval.c = '\v'; break;
      case 'f': delval.c = '\f'; break;
      case 'r': delval.c = '\r'; break;
      default:
	delval.c = yytext[1];
      }
    }
    else
      deerror ("char constant too large");
  }
  .			{
    if (!overflow) {
      overflow = true;
      delval.c = *yytext;
    }
    else
      deerror ("char constant too large");
  }
  \n			{
    lineno++;
    deerror ("unterminated char constant");
    if (!overflow)
      delval.c = 0;
    SC_POP; I; RETURN (CHARACTER);
  }
}

<dechar_oct>[0-7]	{
  (delval.c <<= 3) |= *yytext - '0';
  if (++digits >= 3 || (delval.c >> (CHAR_T_BIT - 3)))
    SC_SWITCH (dechar);
}

<dechar_hex>{
  [0-9] {
    (delval.c <<= 4) |= *yytext - '0';
    if (++digits >= 2)
      SC_SWITCH (dechar);
  }
  [a-f] {
    (delval.c <<= 4) |= *yytext - 'a' + 0x0a;
    if (++digits >= 2)
      SC_SWITCH (dechar);
  }
  [A-F] {
    (delval.c <<= 4) |= *yytext - 'A' + 0x0a;
    if (++digits >= 2)
      SC_SWITCH (dechar);
  }
}

<dechar_oct,dechar_hex>{
  .|\n			SC_SWITCH_B (dechar);
}

<dechar,dechar_oct,dechar_hex><<EOF>>		{
  deerror ("EOF in char constant"); SC_POP; NEXTBUF;
}

\"			{ INITSTR; SC_CALL (destring, NAME); }

<destring>{
  \"			SC_POP;
  \\\n{ws}*		lineno++;
  \\/[0-7]		{
    character = 0, digits = 0; SC_SWITCH (destring_oct);
  }
  \\x/[0-9a-fA-F]	{
    character = 0, digits = 0; SC_SWITCH (destring_hex);
  }
  \\x
  \\.			{
    switch (yytext[1]) {
    case 'a': APPEND ('\a'); break;
    case 'b': APPEND ('\b'); break;
    case 't': APPEND ('\t'); break;
    case 'n': APPEND ('\n'); break;
    case 'v': APPEND ('\v'); break;
    case 'f': APPEND ('\f'); break;
    case 'r': APPEND ('\r'); break;
    default:
      APPEND (yytext[1]);
    }
  }
  \n			{ EXTEND; lineno++; }
  [^\\\"\n]+		EXTEND;
  \\			{
    deerror ("string constant terminated by `\\'"); EXTEND; SC_POP;
  }
  <<EOF>>		{
    deerror ("EOF in string constant"); SC_POP; NEXTBUF;
  }
}

<destring_oct>[0-7]	{
  (character <<= 3) |= *yytext - '0';
  if (++digits >= 3 || (character >> (CHAR_BIT - 3))) {
    APPEND (character);
    SC_SWITCH (destring);
  }
}

<destring_hex>{
  [0-9] {
    (character <<= 4) |= *yytext - '0';
    if (++digits >= 2) {
      APPEND (character);
      SC_SWITCH (destring);
    }
  }
  [a-f] {
    (character <<= 4) |= *yytext - 'a' + 0x0a;
    if (++digits >= 2) {
      APPEND (character);
      SC_SWITCH (destring);
    }
  }
  [A-F] {
    (character <<= 4) |= *yytext - 'A' + 0x0a;
    if (++digits >= 2) {
      APPEND (character);
      SC_SWITCH (destring);
    }
  }
}

<destring_oct,destring_hex>{
  .|\n			{ APPEND (character); SC_SWITCH_B (destring); }
  <<EOF>>		{
    APPEND (character); SC_POP;
    deerror ("EOF at escaped character in string constant"); NEXTBUF;
  }
}

;				{ I; return *yytext; }
[!#%&()*+,\-./:<=>?\[\]^{|}~]	{ I; RETURN (*yytext); }
\\\n			lineno++;
.			deerror ("unknown character");

<INITIAL><<EOF>>	NEXTBUF;

%%

static const char* lastscriptname;
static const char* lastscriptstring;

void
delexreset (void)
{
  lineno = 1;
  filename.create (0);
  lastscriptname = 0;
  stringptr = lastscriptstring = 0;
  overflow = false;
  condlevel = passlevel = 0;
  pass = true;
  thePrinter.setBOL (&demsg);
  SC_FLUSH;
  while (!in.empty ())
    popInclude ();
  yy_init = 1;
  struct yy_buffer_state* b = YY_CURRENT_BUFFER;
  yy_delete_buffer (b);
  if (yy_start_stack) {
    free (yy_start_stack);
    yy_start_stack = 0;
    yy_start_stack_depth = 0;
  }
}

unsigned
demsg (void)
{
  return fprintf (stderr, "%s:%u:", filename.getString (), lineno);
}

void
dewarn (const char* s)
{
  if (dewarnings)
    fprintf (stderr, "%s:%u:Warning:%s\n", filename.getString (), lineno, s);
}

void
deerror (const char* s)
{
  if (!strcmp (s, "parse error"))
    command = true;
  fprintf (stderr, "%s:%u:%s\n", filename.getString (), lineno, s);
}

static bool
popInclude (void)
{
  {
    size_t oldcondlevel = in.empty () ? 0 : in.top ().condlevel;

    if (oldcondlevel < condlevel) {
      deerror ("too many #if statements");
    }
    else if (oldcondlevel > condlevel) {
      deerror ("too many #endif statements");
    }
  }

  if (dein && dein != stdin) fclose (dein);

  if (in.empty ()) {
    yyrestart (0);
    if (lastscriptname == scriptname && lastscriptstring == scriptstring) {
      filename = scriptname;
      return false;
    }
    else if (filename = lastscriptname = scriptname,
	     scriptname == scriptcommand) {
      if (lastscriptstring == scriptstring) {
	yy_init = 1;
	return false;
      }
      stringptr = lastscriptstring = scriptstring;
      return true;
    }
    else if (!(dein = openscript (lastscriptname))) {
      perror (lastscriptname);
      yy_init = 1;
      return false;
    }
    else {
      lineno = 1;
      yyrestart (dein);
      return true;
    }
  }
  else {
    filename = *in.top ().name;
    lineno = in.top ().lineno;
    delete in.top ().name;
    yy_delete_buffer (YY_CURRENT_BUFFER);
    yy_switch_to_buffer (in.top ().bs);
    in.pop ();
    return true;
  }
}
