/* -*- c++ -*- Scanner for Maria's Petri Net language */

%option nostdinit
%option stack
%option prefix="pn"
%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 "typedefs.h"
# include "maria.tab.h"

# include "StringBuffer.h"
# include "util.h"
# include "NameList.h"

  /** @file maria.lex
   * Lexical analyser for the modelling language
   */

  /* Copyright © 1998-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. */

# 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;
  /** Set of defined preprocessor conditions */
  static class NameList defs;
  /** 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 (pnlval.s = sbuf.copy ()) == sbuf.getLength ()) ||	\
  (pnerror ("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* netname;

  /** 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 pnlexreset (void);

  /** warning flag (true=print warnings) */
  bool pnwarnings = true;
  /** number of errors encountered */
  unsigned pnerrors = 0;

  /** The place that is currently being processed */
  extern class Place* currentPlace;
  class Net;
# include "Place.h"
  /** The transition that is currently being processed */
  extern class Transition* currentTransition;
# include "Transition.h"

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

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

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

  /** Open an input file.
   * @param name	name of the input file
   * @return		a descriptor to the file, or NULL on error
   */
  extern FILE* openfile (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
   */
  bool defined (const char* sym);
  /** Define a preprocessor symbol
   * @param sym		name of the symbol
   */
  void define_sym (const char* sym);
  /** Undefine a preprocessor symbol
   * @param sym		name of the symbol
   */
  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 (!pnin && !popInclude ()) result = 0, pnlexreset ();		\
  else if (!(result = fread (buf, 1, max_size, pnin)) && ferror (pnin))	\
    pnerror ("input error"), pnlexreset ()
%}

/* 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 pnname
/* parsing a character constant */
%x pnchar
/* parsing an octal character constant */
%x pnchar_oct
/* parsing a hexadecimal character constant */
%x pnchar_hex
/* parsing a string constant */
%x pnstring
/* parsing an octal character constant in a string */
%x pnstring_oct
/* parsing a hexadecimal character constant in a string */
%x pnstring_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>>		{ pnerror ("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)
      pnerror ("#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)
      pnerror ("#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_]* {
    pnerror ("unknown preprocessor directive");
    SC_SWITCH (pr_ignore);
  }
  !			{
    SC_SWITCH (pr_ignore); /* #! interpreter command */
  }
  .			{
    pnerror ("error in preprocessor directive"); SC_SWITCH (pr_ignore);
  }
  \n			{
    pnerror ("expected a preprocessor directive"); SC_POP; lineno++;
  }
}

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

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

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

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

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

<pr_incl_name>.|\n	{
  SC_POP_B; I;
  if (FILE* f = openfile (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 ());
    pnerror ("could not open include file");
  }
}

<pr_cond>{
  [A-Za-z_][A-Za-z0-9_]*	{
    switch (currentDirective) {
    case none:
      pnerror ("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)
      pnerror ("missing condition name");
    lineno++; SC_POP;
  }

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

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

"enum"/{nn}		{ I; return ENUM; }
"id"/{nn}		{ I; return ID; }
"union"/{nn}		{ I; return UNION; }
"queue"/{nn}		{ I; return QUEUE; }
"stack"/{nn}		{ I; return STACK; }
"is"/{nn}		{ I; return IS; }
"struct"/{nn}		{ I; return STRUCT; }
"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; }
"strongly_fair"/{nn}	{ I; return SFAIR; }
"weakly_fair"/{nn}	{ I; return WFAIR; }
"enabled"/{nn}		{ I; return ENABLED; }
"hide"/{nn}		{ I; return HIDE; }
"prop"/{nn}		{ I; return PROP; }

"typedef"/{nn}		{ I; return TYPEDEF; }
"const"/{nn}		{ I; return CONST_; }

"subnet"/{nn}		{ I; return SUBNET; }
"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; }
"reject"/{nn}		{ I; return REJECT_; }
"deadlock"/{nn}		{ I; return DEADLOCK; }
"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; }
".."		{ I; return RANGE; }

0x		{
  pnlval.i = 0, overflow = false; SC_CALL (hexadecimal, NUMBER);
}
0		{
  pnlval.i = 0, overflow = false; SC_CALL (octal, NUMBER);
}
[1-9]		{
  pnlval.i = *yytext - '0', overflow = false; SC_CALL (decimal, NUMBER);
}

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

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

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

<hexadecimal,octal,decimal>{
  [0-9a-zA-Z_]	{
    pnerror ("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 (pnname); }
\\.			{ INITSTR; APPEND (yytext[1]); SC_PUSH (pnname); }

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

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

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

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

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

<pnchar_oct,pnchar_hex>{
  .|\n			SC_SWITCH_B (pnchar);
}

<pnchar,pnchar_oct,pnchar_hex><<EOF>>		{
  pnerror ("EOF in char constant"); SC_POP; NEXTBUF;
}

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

<pnstring>{
  \"			SC_POP;
  \\\n{ws}*		lineno++;
  \\/[0-7]		{
    character = 0, digits = 0; SC_SWITCH (pnstring_oct);
  }
  \\x/[0-9a-fA-F]	{
    character = 0, digits = 0; SC_SWITCH (pnstring_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;
  \\			{
    pnerror ("string constant terminated by `\\'"); EXTEND; SC_POP;
  }
  <<EOF>>		{
    pnerror ("EOF in string constant"); SC_POP; NEXTBUF;
  }
}

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

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

<pnstring_oct,pnstring_hex>{
  .|\n			{ APPEND (character); SC_SWITCH_B (pnstring); }
  <<EOF>>		{
    APPEND (character); SC_POP;
    pnerror ("EOF at escaped character in string constant"); NEXTBUF;
  }
}

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

<INITIAL><<EOF>>	NEXTBUF;

%%

void
pnlexreset (void)
{
  lineno = 1;
  filename.create (0);
  overflow = false;
  condlevel = passlevel = 0;
  pass = true;
  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
pnmsg (void)
{
  unsigned col =
    fprintf (stderr, "%s:%u:", filename.getString (), lineno);
  if (currentTransition)
    col += fprintf (stderr, "transition `%s':", currentTransition->getName ());
  else if (currentPlace)
    col += fprintf (stderr, "place `%s':", currentPlace->getName ());
  return col;
}

void
pnwarn (const char* s)
{
  if (pnwarnings) {
    fprintf (stderr, "%s:%u:Warning:", filename.getString (), lineno);
    if (currentTransition)
      fprintf (stderr, "transition `%s':", currentTransition->getName ());
    else if (currentPlace)
      fprintf (stderr, "place `%s':", currentPlace->getName ());
    fprintf (stderr, "%s\n", s);
  }
}

void
pnerror (const char* s)
{
  fprintf (stderr, "%s:%u:", filename.getString (), lineno);
  if (currentTransition)
    fprintf (stderr, "transition `%s':", currentTransition->getName ());
  else if (currentPlace)
    fprintf (stderr, "place `%s':", currentPlace->getName ());
  fprintf (stderr, "%s\n", s);
  pnerrors++;
}

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

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

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

  if (in.empty ()) {
    static const char* lastnetname;
    filename = netname;
    if (lastnetname == netname)
      return yyrestart (0), false;
    else if (!(pnin = openfile (lastnetname = netname))) {
      perror (lastnetname);
      pnerrors++;
      yy_init = 1;
      return yyrestart (0), false;
    }
    else {
      lineno = 1;
      return yyrestart (pnin), 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;
  }
}

bool
defined (const char* sym)
{
  for (NameList::const_iterator i = defs.begin (); i != defs.end (); i++)
    if (!strcmp (sym, *i))
      return true;
  return false;
}

void
define_sym (const char* sym)
{
  if (!defined (sym))
    defs.append (newString (sym));
}

void
undef_sym (const char* sym)
{
  for (NameList::iterator i = defs.begin (); i != defs.end (); i++) {
    if (!strcmp (sym, *i)) {
      defs.remove (i);
      return;
    }
  }
}
