/* -*- c++ -*- Parser for the high-level Petri Net language used in Maria */

%{
#ifndef BSD
# ifdef __APPLE__
#  define BSD
# elif defined __FreeBSD__||defined __OpenBSD__||defined __NetBSD__
#  define BSD
# elif defined _AIX
#  define BSD
# endif
#endif
#if !defined __WIN32 && !defined __CYGWIN__ && !defined BSD
# include <alloca.h>
#endif // !__WIN32 && !__CYGWIN__ && !BSD

#include "util.h"
#include "Printer.h"
#include "VariableStackMap.h"

#include "allExpressions.h"
#include "ExpressionSet.h"
#include "Constraint.h"
#include "Range.h"

#include "Arc.h"
#include "Transition.h"
#include "Place.h"
#include "Net.h"
#include "VariableDefinition.h"
#include "VariableSet.h"
#include "Function.h"

#include "Quantifier.h"

#include "TransitionQualifier.h"

#include "allTypes.h"
#include "allValues.h"
#include "Valuation.h"

# include <set>
# include <map>
# include <list>
# include <stack>

# ifndef NDEBUG
#  define ASSERT1(x) x
# else
#  define ASSERT1(x)
# endif

  /** @file maria.y
   * Parser 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. */

  /** warning flag (true=print warnings) */
  extern bool pnwarnings;
  /** number of errors encountered */
  extern unsigned pnerrors;
  /** Maximum index type size of arrays */
  card_t maxIndex = 256;
  /** Maximum range size of quantifications */
  card_t maxRange = 256;

  /** reset the lexical analyzer */
  extern void pnlexreset ();

  /** lexical analyzer function
   * @return		a terminal symbol, and set yylval to its semantic value
   */
  extern int pnlex ();

  /** Check the validity of an identifier
   * @param name	name of the identifier
   * @param type	type of the identifier
   * @param msg		error message
   */
  extern void checkName (const char* name, unsigned type, const char* msg);
  /** validity check codes for checkName */
  enum checkNameType {
    ckPlace = 0, ckTrans, ckType, ckEnum, ckComp,
    ckFunc, ckParam, ckVar, ckIter
  };

  /** The Petri Net */
  extern class Net* net;
# define theNet (*net)
  /** The printer object */
  extern class Printer thePrinter;
  /** Parameters of the function that is currently being processed */
  static class VariableDefinition** currentParameters = NULL;
  /** Arity of the function that is currently being processed */
  static unsigned currentArity = 0;
  /** Function being invoked */
  static std::stack< class Function*, std::list<class Function*> >
    functionStack;
  /** The place that is currently being processed */
  class Place* currentPlace = NULL;
  /** The transition that is currently being processed */
  class Transition* currentTransition = NULL;
  /** The context transition (not being processed) */
  static class Transition* currentContextTransition = NULL;
  /** The type definition that is currently being processed */
  static const class Type* currentType = NULL;
  /** The context type of currentType (StructType or VectorType or NULL) */
  static const class Type* currentContextType = NULL;
  /** Struct or vector component number currently being parsed */
  static card_t currentComponent;
  /** Type context */
  struct TypeContext
  {
    /** Context type (@see currentContextType), may be NULL */
    const class Type* context;
    /** Component type (@see currentContext) */
    const class Type* component;
    /** Number of current component (@see currentComponent)
     * , meaningless if context==NULL */
    card_t numComponent;
  };
  /** Stack of types, used e.g. when parsing quantifier conditions */
  static std::stack< struct TypeContext, std::list<struct TypeContext> >
    typeStack;
  /** Flag: is an output arc being parsed? */
  static bool isOutputArc = false;
  /** Flag: has a number been successfully parsed? */
  static bool numberParsed = false;

  /** Flag: has an empty list component been parsed? */
  static bool emptyParsed = false;

  /** type of constraint being parsed */
  static enum Net::CKind isFairnessStrong;

  typedef std::pair<const class Type*,card_t> StructLevel;
  /** Stack for parsing structs and vectors */
  static std::stack< StructLevel, std::list<StructLevel> > structLevels;

  /** check the validity of an iterator variable name
   * @param name	name of the identifier variable
   */
  static void checkIterator (const char* name);

  /** Make a copy of a type if it is not an unnamed user type
   * @param type	the type to be copied
   * @return		type or a copy of it
   */
  static class Type* copyType (class Type* type);

  /** Delete the type definition if it is an unnamed user type
   * @param type	the type to be deleted
   */
  static void deleteType (class Type* type);

  /** Display a type name in an error message
   * @param type	type to be displayed
   */
  static void printType (const class Type& type);

  /** Push the type context on structLevels
   * @param type	new type context
   */
  static void pushTypeContext (const class Type* type);
  /** Restore the type context from structLevels */
  static void popTypeContext ();

  /** Get the parameter of the currently parsed function by name
   * @param name	name of the parameter
   * @return		the corresponding VariableDefinition, or NULL
   */
  static const class VariableDefinition* getParameter (char* name);

  /** Add a parameter to the function definition being parsed
   * @param type	type of the parameter
   * @param name	name of the parameter
   */
  static void addParameter (const class Type* type, char* name);

  /** Get a Type by name
   * @param name	the type name
   * @return		a Type object
   */
  static const class Type* getType (const char* name);

  /** Get a Function by name
   * @param name	the function name
   * @return		a Function object
   */
  static class Function* getFunction (const char* name);

  /** Get a zero-arity Function by name
   * @param name	the function name
   * @return		a Function object
   */
  static class Function* getConstantFunction (const char* name);
  
  /** Get a Constant by name
   * @param name	the constant name
   * @return		a Constant object
   */
  static const class Constant* getConstant (const char* name);

  /** Get a VariableDefinition by name
   * @param name	the variable name
   * @return		a VariableDefinition object
   */
  static const class VariableDefinition* getVariable (char* name);

  /** Resolve a name to a constant or variable
   * @param name	the name
   * @return		the resolved expression, or NULL
   */
  static class Expression* resolveName (char* name);

  /** Ensure that the formula is a set-valued expression
   * @param expr	formula to be checked
   * @param type	expected type of the formula
   * @return		expr, pointer to a Marking object, or NULL
   */
  static class Expression* ensureSet (class Expression* expr,
				      const class Type* type = 0);

  /** Ensure that the formula is a marking expression, converting if needed
   * @param expr	formula to be checked
   * @param type	expected type of the expression
   * @param temporal	flag: allow expr to be temporal
   * @return		pointer to a Marking object, or NULL
   */
  static class Marking* ensureMarking (class Expression* expr,
				       const class Type* type,
				       bool temporal = false);

  /** Ensure that the formula is not a marking expression
   * @param expr	formula to be checked
   * @return		expr or NULL
   */
  static class Expression* noMarking (class Expression* expr);

  /** Ensure that the formula is a basic expression
   * @param expr	formula to be checked
   * @return		expr or NULL
   */
  static class Expression* ensureExpr (class Expression* expr);

  /** Ensure that the formula is a boolean expression
   * @param expr	formula to be checked
   * @param undefined	flag: allow the formula to be Undefined
   * @return		expr or NULL
   */
  static class Expression* ensureBool (class Expression* expr,
				       bool undefined = false);

  /** Ensure that the formula is a signed integer expression
   * @param expr	formula to be checked
   * @return		expr or NULL
   */
  static class Expression* ensureInt (class Expression* expr);

  /** Ensure that the formula is an unsigned integer expression
   * @param expr	formula to be checked
   * @return		expr or NULL
   */
  static class Expression* ensureCard (class Expression* expr);

  /** Ensure that the formula is a signed or unsigned integer expression
   * @param expr	formula to be checked
   * @return		expr or NULL
   */
  static class Expression* ensureIntCard (class Expression* expr);

  /** Ensure that the formula is a buffer expression
   * @param expr	formula to be checked
   * @return		expr or NULL
   */
  static class Expression* ensureBuffer (class Expression* expr);

  /** Ensure that the formula is a basic expression of ordered type
   * @param expr	formula to be checked
   * @return		expr or NULL
   */
  static class Expression* ensureOrdered (class Expression* expr);

  /** Add an enumeration constant to an enumerated type, ensuring uniqueness.
   * @param e		the enumeration type
   * @param name	name of the constant
   */
  static void addEnumeration (class EnumType* e, char* name);

  /** Add an enumeration constant to an enumerated type, ensuring uniqueness.
   * @param e		the enumeration type
   * @param name	name of the constant
   * @param value	value of the constant
   */
  static void addEnumeration (class EnumType* e, char* name, card_t value);

  /** Ensure that the formula is a constant literal
   * @param expr	formula to be checked
   * @param type	type of the constant expected
   * @return		the value of expr or NULL
   */
  static class Value* ensureConstant (class Expression* expr,
				      const class Type* type);

  /** Convert an integer to an enumeration value if possible
   * @param type	type of the enumeration value
   * @param value	numerical value to be converted to the enumeration type
   * @return		a constant having the enumeration value or NULL
   */
  static class Expression* enumValue (const class EnumType& type,
				      card_t value);

  /** Ensure that a named component matches a struct being parsed
   * @param name	name of the component
   * @param expr	initialization expression for the component
   * @return		expr or NULL
   */
  static class Expression* namedComponent (char* name, class Expression* expr);

  /** Begin parsing a non-determinism expression
   * @param type	type of the expression
   * @param hidden	flag: hide the variable from transition instances
   * @param name	name of the non-deterministic variable (optional)
   */
  static void beginAny (class Type*& type, char* name, bool hidden);

  /** End parsing a non-determinisim expression
   * @param type	type of the expression
   * @param name	name of the non-deterministic variable (optional)
   * @param condition	quantification condition (optional)
   * @return		a Variable expression for the variable, or NULL
   */
  static class Variable* endAny (const class Type* type, char* name,
				 class Expression* condition);

  /** Begin parsing a quantifier expression
   * @param name	name of the quantifier variable
   * @param type	type of the quantifier variable
   */
  static void beginQuantifier (char* name, const class Type* type);

  /** Continue parsing a quantifier expression
   *  (after parsing an optional condition)
   * @param type	type of the quantifier variable
   */
  static void continueQuantifier (ASSERT1 (const class Type* type));

  /** End parsing a quantifier expression
   * @param name	name of the quantifier variable
   * @param type	type of the quantifier variable
   * @param condition	quantification condition (optional)
   * @param kind	quantification kind (1=sum, 2=forall, 3=exists)
   * @param expr	expression
   * @param temporal	flag: allow expr to be temporal
   * @return		expr augmented with the quantifier, or NULL if failed
   */
  static class Expression* endQuantifier (char* name, const class Type* type,
					  class Expression* condition,
					  unsigned kind,
					  class Expression* expr,
					  bool temporal);

  /** Declare a quantified variable
   * @param prefix	prefix of the quantified variable
   * @param quant	name of the quantifier variable
   * @param p		flag: use a predecessor value of the quantifier
   * @return		a quantified variable, or NULL if failed
   */
  static class Expression* quantifierVariable (char* prefix,
					       char* quant,
					       bool p);

  /** Try to make expr compatible with type if expr is a Constant
   * @param expr	expression to be made compatible
   * @param type	type to be made compatible with
   * @param casting	flag: typecasting in progress (suppress a warning)
   * @return		true if expr is not a Constant or
   *			if the conversion succeeded
   */
  static bool makeCompatible (class Expression*& expr,
			      const class Type& type,
			      bool casting = false);

  /** Determine whether an expression is compatible with a type
   * @param expr	expression to be checked
   * @param type	the type
   * @return		true if the expression is compatible with the type
   */
  static bool isCompatible (class Expression*& expr,
			    const class Type* type);

  /** Determine whether an expression is compatible with
   * the struct component being parsed
   * @param expr	expression to be checked
   * @param s		the struct whose current component is considered
   * @return		true if the expression is compatible with the struct
   */
  static bool isCompatible (class Expression*& expr,
			    const class StructExpression& s);

  /** Ensure that the type is ordered
   * @param type	type to be checked
   * @return		true if the type is ordered
   */
  static bool isOrdered (const class Type& type);

  /** Check a valuation
   * @param v		valuation to be checked
   * @return		true if the valuation is OK
   */
  static bool check (const class Valuation& v);

  /** Perform constant folding
   * @param expr	expression to be folded
   * @return		expr or a new Constant, or NULL in case of error
   */
  static class Expression* fold (class Expression* expr);

  /** Define a function
   * @param type	type of the function
   * @param name	name of the function
   * @param expr	body of the function
   */
  static void defun (const class Type* type,
		     char* name,
		     class Expression* expr);

  /** Create a RelopExpression or optimize it
   * @param equal	flag: equality comparison (instead of less-than)
   * @param left	left-hand-side of RelopExpression
   * @param right	right-hand-side of RelopExpression
   * @return		RelopExpression or a Boolean constant
   */
  static class Expression* newRelopExpression (bool equal,
					       class Expression* left,
					       class Expression* right);

  /** Create a BinopExpression
   * @param op		operator
   * @param left	left-hand-side of BinopExpression
   * @param right	right-hand-side of BinopExpression
   * @return		BinopExpression
   */
  static class Expression* newBinopExpression (enum BinopExpression::Op op,
					       class Expression* left,
					       class Expression* right);

  /** Create a NotExpression or optimize it
   * @param expr	an expression
   * @return		a negation of the expression
   */
  static class Expression* newNotExpression (class Expression* expr);

  /** Create a BooleanBinop or optimize it
   * @param conj	flag: conjunction (instead of disjunction)
   * @param left	left-hand-side of BooleanBinop
   * @param right	right-hand-side of BooleanBinop
   * @return		left, right, Boolean constant, or a BooleanBinop
   */
  static class Expression* newBooleanBinop (bool conj,
					    class Expression* left,
					    class Expression* right);

  /** Create a SetExpression or optimize it
   * @param op		operator
   * @param left	left-hand-side of SetExpression
   * @param right	right-hand-side of SetExpression
   */
  static class Expression* newSetExpression (enum SetExpression::Op op,
					     class Expression* left,
					     class Expression* right);
  /** Create a CardinalityExpression or optimize it
   * @param op		operator
   * @param expr	the multi-set expression
   */
  static class Expression* newCardinalityExpression
    (enum CardinalityExpression::Op op,
     class Expression* expr);

  /** Create a BufferUnop from a buffer expression
   * @param op		operator
   * @param expr	the buffer expression
   * @return		a BufferUnop object, or NULL
   */
  static class Expression* newBufferUnop (enum BufferUnop::Op op,
					  class Expression* expr);

  /** Variable stack map to be used with variable scoping */
  static class VariableStackMap currentVariables;

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

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

%union{
  card_t i;
  bool flag;
  char_t c;
  char* s;
  class Expression* expr;
  class ExpressionList* exprList;
  class Transition* trans;
  class Place* place;
  class Marking* marking;
  class Type* type;
  class ComponentList* componentList;
  class Value* value;
  class Range* range;
  class Constraint* constraint;
}

%right '?' ':'
%left UNTIL RELEASE
%left IMPL EQUIV
%left OR
%left XOR
%left AND
%left FINALLY GLOBALLY NEXT
%left '|'
%left '^'
%left '&'
%left NE EQ
%left GE LE '<' '>'
%left ROL ROR
%left '+' '-'
%left '*' '/' '%'
%right '#' IS
%left '~' '!'
%right CARDINALITY MIN_ MAX_ MAP
%left EQUALS
%left SUBSET
%left MINUS UNION
%left INTERSECT
%left ATOM_
%left '.' '[' '('

%token <i>
	NUMBER

%token <c>
	CHARACTER

%token <s>
	NAME

%token
	TRUE_ FALSE_ UNDEFINED FATAL
	TYPEDEF CONST_ PLACE TRANS IN_ OUT_ GATE
	REJECT_ DEADLOCK
	ENUM ID STRUCT QUEUE STACK RANGE EMPTY SFAIR WFAIR ENABLED HIDE PROP
	SUBNET

%type <s>
	name
	_opt_name
	name_
	enum_name
	comp_name
	func_name

%type <i>
	number
	quantifier
	fairness_strong/* 0=weakly_fair, 1=strongly_fair, 2=enabled */

%type <expr>
	_opt_expr
	_opt__expr
	expr_
	expr
	formula
	marking_list_
	qual_expr
	struct_expr

%type <exprList>
	arg_list
	colon_formula

%type <marking>
	place_init
	marking
	marking_list

%type <place>
	placename

%type <trans>
	transname

%type <type>
	typedefinition
	typereference
	enum_list

%type <componentList>
	comp_list

%type <constraint>
	range_list
	constraint
	_opt_capacity
%type <range>
	range
%type <value>
	value
	value_

%type <flag>
	_list_var_expr/* true if component present */
	_opt_colon/* true if present */
	_opt_exclam/* true if present */
	_opt_place/* true if present */
	_opt_trans/* true if present */
	_opt_hide/* true if present */
	_opt_const/* true if present */
	queue_stack/* false=queue, true=stack */

%%

start: _opt_net
	{
	  pnlexreset ();
	}
	;

_opt_net:
	|
	net
	;

net:	';'
	{ pnwarn ("extraneous semicolon"); }
	|
	net ';'
	{ pnwarn ("extraneous semicolon"); }
	|
	netcomponent ';'
	|
	net netcomponent ';'
	;

netcomponent:
	type
	|
	function
	|
	place
	|
	transition
	|
	fairness
	|
	verify
	|
	prop
	|
	subnet
	;

type:
	TYPEDEF typedefinition name
	{
	  if ($3 && getType ($3)) {
	    thePrinter.printRaw ("ignoring redefinition of type ");
	    thePrinter.printQuoted ($3);
	    thePrinter.finish ();
	    pnerrors++;
	    deleteType ($2); delete[] $3;
	  }
	  else if ($2 && $3) {
	    checkName ($3, ckType, "invalid type name ");
	    theNet.addType (*$2, $3);
	  }
	  else {
	    deleteType ($2); delete[] $3;
	  }
	}
	;

queue_stack:
	QUEUE
	{ $$ = false; }
	|
	STACK
	{ $$ = true; }
	|
	queue_stack QUEUE
	{ $$ = $1; pnerror ("ignoring extraneous `queue'"); }
	|
	queue_stack STACK
	{ $$ = $1; pnerror ("ignoring extraneous `stack'"); }
	;

typedefinition:
	ENUM '{' enum_list '}'
	{
	  if (!($$ = $3))
	    $$ = new class StructType (*new class ComponentList);
	}
	|
	ID '[' number ']'
	{
	  if (numberParsed && $3 > 0)
	    $$ = new class IdType ($3);
	  else {
	    if (numberParsed)
	      pnerror ("empty identifier pool");
	    $$ = NULL;
	  }
	}
	|
	STRUCT '{' comp_list '}'
	{ $$ = $3 ? new class StructType (*$3) : NULL; emptyParsed = false; }
	|
	UNION '{' comp_list '}'
	{
	  if ($3 && !$3->getSize ()) {
	    pnerror ("missing component list for union");
	    delete $3, $$ = NULL;
	  }
	  else
	    $$ = $3 ? new class UnionType (*$3) : NULL;

	  emptyParsed = false;
	}
	|
	typereference
	{ $$ = $1; }
	|
	typedefinition
	{ currentType = $1; }
	constraint
	{
	  if (($$ = $1) && $3) {
	    $$ = copyType ($$);
	    if (class Constraint* c = $$->getConstraint ()) {
	      if (c->isDisjoint (*$3)) {
		pnerror ("ignoring constraint that would make the type empty");
		delete $3;
	      }
	      else
		c->restrict (*$3, *$$);
	    }
	    else
	      $$->setConstraint (*$3);
	  }
 	  else
 	    delete $3;
	  currentType = NULL;
	}
	|
	typedefinition '[' typedefinition ']'
	{
	  if ($3 && !isOrdered (*$3)) {
	    deleteType ($1), deleteType ($3), $$ = NULL;
	  }
	  else if ($1 && $3) {
	    card_t size = $3->getNumValues ();
	    if (!size || size == CARD_T_MAX || (maxIndex && size > maxIndex)) {
	      thePrinter.printRaw ("type ");
	      printType (*$3);
	      thePrinter.printRaw (" has too big domain for indexing");
	      if (size && size < CARD_T_MAX)
		thePrinter.printRaw (" (try -a0)");
	      thePrinter.finish ();
	      pnerrors++;
	      deleteType ($1), deleteType ($3), $$ = NULL;
	    }
	    else {
	      theNet.addType (*$1); theNet.addType (*$3);
	      $$ = new class VectorType (*$1, *$3);
	    }
	  }
	  else
	    deleteType ($1), deleteType ($3), $$ = NULL;
	}
	|
	typedefinition '[' queue_stack number ']'
	{
	  if ($1 && numberParsed) {
	    if (!$4) {
	      pnerror ("empty buffer");
	      deleteType ($1), $$ = NULL;
	    }
	    else {
	      theNet.addType (*$1);
	      $$ = new class BufferType (*$1, $4, $3);
	    }
	  }
	  else
	    deleteType ($1), $$ = NULL;
	}
	|
	typedefinition '[' NUMBER ']'
	{
	  pnerror ("array dimension must be an ordered type");
	  deleteType ($1), $$ = NULL;
	}
	;

constraint:
	'(' range_list ')'
	{ $$ = $2; }
	;

range_list:
	range_list delim range
	{
 	  if (($$ = $1) && $3)
 	    $$->append (*$3);
 	  else
	    delete $3;
	}
	|
	range
	{ if ($1) ($$ = new class Constraint)->append (*$1); else $$ = NULL; }
	;

range:
	value_
	{ $$ = $1 ? new class Range (*$1, *$1) : NULL; }
	|
	RANGE value_
	{ $$ = $2 ? new class Range (currentType->getFirstValue (), *$2) : 0; }
	|
	value RANGE value
	{
 	  if ($1 && $3) {
	    if (pnwarnings && *$3 < *$1)
	      pnwarn ("upper limit of range specified before lower limit");
 	    $$ = new class Range (*$1, *$3);
	  }
	  else {
	    $$ = NULL; delete $1; delete $3;
	  }
	}
	|
	value RANGE
	{ $$ = $1 ? new class Range (*$1, currentType->getLastValue ()) : 0; }
	;

value:
	expr
	{ $$ = ensureConstant ($1, currentType); }
	;

value_:
	expr_
	{ $$ = ensureConstant ($1, currentType); }
	;

typereference:
	name_
	{
	  if (!$1) $$ = NULL;
	  else if (($$ = const_cast<class Type*>(getType ($1))));
	  else if (!strcmp ($1, "bool"))
	    $$ = &const_cast<class BoolType&>(Net::getBoolType ());
	  else if (!strcmp ($1, "int"))
	    $$ = &const_cast<class IntType&>(Net::getIntType ());
	  else if (!strcmp ($1, "unsigned"))
	    $$ = &const_cast<class CardType&>(Net::getCardType ());
	  else if (!strcmp ($1, "char"))
	    $$ = &const_cast<class CharType&>(Net::getCharType ());
	  else {
	    thePrinter.printRaw ("undefined type ");
	    thePrinter.printQuoted ($1);
	    thePrinter.finish ();
	    pnerrors++;
	  }
	  delete[] $1;
	}
	;

number:
	expr
	{
	  $$ = 0; numberParsed = false;

	  if ($1 && $1->getType () && ($1 = fold ($1)) &&
	      $1->getKind () == Expression::eConstant) {
	    const class Valuation empty;
	    class Value* v = $1->eval (empty);
	    assert (v && $1->getType ()->isConstrained (*v));
	    switch (v->getType ().getKind ()) {
	    case Type::tChar:
	      $$ = char_t (static_cast<const class LeafValue&>(*v));
	      if (pnwarnings) {
		thePrinter.printRaw ("Warning:converting ");
		thePrinter.print (char_t ($$));
		thePrinter.printRaw (" to ");
		thePrinter.print ($$);
		thePrinter.finish ();
	      }
	      break;
	    case Type::tInt:
	      numberParsed = true;
	      $$ = int_t (static_cast<const class LeafValue&>(*v));
	      if (pnwarnings) {
		thePrinter.printRaw ("Warning:converting ");
		thePrinter.print (int_t ($$));
		thePrinter.printRaw (" to ");
		thePrinter.print ($$);
		thePrinter.finish ();
	      }
	      break;
	    case Type::tCard:
	      numberParsed = true;
	      $$ = card_t (static_cast<const class LeafValue&>(*v));
	      break;
	    case Type::tEnum:
	      numberParsed = true;
	      $$ = card_t (static_cast<const class LeafValue&>(*v));
	      break;
	    default:
	      pnerror ("expression does not evaluate to a number");
	      break;
	    }

	    delete v;
	  }
	  else if ($1)
	    pnerror ("number expected, got expression");

	  $1->destroy ();
	}
	;

name_:
	NAME
	{ $$ = $1; }
	|
	error
	{ $$ = NULL; }
	;

_opt_name:
	{ $$ = NULL; }
	|
	name_
	{ $$ = $1; }
	;

name:
	{ pnerror ("missing name"); $$ = NULL; }
	|
	name_
	{ $$ = $1; }
	;

enum_name:
	name_
	{
	  if (($$ = $1))
	    checkName ($$, ckEnum, "invalid enumeration constant name ");
	}
	;

_opt_eq:
	|
	'='
	;

enum_list:
	{ $$ = NULL; pnerror ("missing enumeration value"); }
	|
	enum_name
	{
	  if ($1)
	    addEnumeration (static_cast<class EnumType*>($$ =
							 new class EnumType),
			    $1);
	  else
	    $$ = NULL;
	}
	|
	enum_list delim enum_name
	{
	  if (!($$ = $1) && $3)
	    addEnumeration (static_cast<class EnumType*>($$ =
							 new class EnumType),
			    $3);
	  else if ($$ && $3)
	    addEnumeration (static_cast<class EnumType*>($$), $3);
	}
	|
	enum_name _opt_eq number
	{
	  if ($1 && numberParsed)
	    addEnumeration (static_cast<class EnumType*>($$ =
							 new class EnumType),
			    $1, $3);
	  else {
	    $$ = NULL; delete[] $1;
	  }
	}
	|
	enum_list delim enum_name _opt_eq number
	{
	  if (!numberParsed) {
	    $$ = $1; delete[] $3;
	  }
	  else if (!($$ = $1) && $3)
	    addEnumeration (static_cast<class EnumType*>($$ =
							 new class EnumType),
			    $3, $5);
	  else if ($$ && $3)
	    addEnumeration (static_cast<class EnumType*>($$), $3, $5);
	}
	;

delim:	';' | ',' ;

comp_name:
	name
	{
	  if (($$ = $1))
	    checkName ($$, ckComp, "invalid component name ");
	}
	;

comp_list:
	typedefinition comp_name
	{
	  if ($1 && $2) {
	    ($$ = new class ComponentList)->addComponent ($2, *$1);
	    theNet.addType (*$1);
	  }
	  else {
	    deleteType ($1);
	    delete[] $2;
	    $$ = NULL;
	  }

	  emptyParsed = false;
	}
	|
	comp_list delim typedefinition comp_name
	{
	  if (($$ = $1) && $3 && $4) {
	    if (emptyParsed)
	      pnerror ("extraneous delimiter in component list");

	    emptyParsed = false;

	    if ((*$$)[$4]) {
	      thePrinter.printRaw ("ignoring redefinition of component ");
	      thePrinter.printQuoted ($4);
	      thePrinter.finish ();
	      pnerrors++;
	      deleteType ($3);
	      delete[] $4;
	    }
	    else {
	      $$->addComponent ($4, *$3);
	      theNet.addType (*$3);
	    }
	  }
	  else {
	    delete $$;
	    deleteType ($3);
	    delete[] $4;
	    $$ = NULL;
	  }
	}
	|
	comp_list delim
	{
	  $$ = $1;
	  if (emptyParsed)
	    pnerror ("extraneous delimiter in component list");
	  emptyParsed = true;
	}
	|
	{ $$ = new class ComponentList; emptyParsed = true; }
	;

next_eq:
	NEXT
	|
	'='
	;

func_name:
	name
	{
	  if (($$ = $1))
	    checkName ($1, ckFunc, "invalid function name ");
	}
	;

function:
	typereference func_name next_eq
	{ currentType = $1; }
	formula
	{ defun ($1, $2, $5); }
	|
	typereference func_name '(' param_list ')'
	{
	  currentType = $1;
	  if (emptyParsed && currentArity)
	    pnerror ("extraneous delimiter after formal parameter list");
	  emptyParsed = false;
	}
	formula
	{ defun ($1, $2, $7); }
	;

param_list:
	typereference name
	{
	  if (currentArity || currentParameters)
	    delete[] $2;
	  else
	    addParameter ($1, $2), emptyParsed = false;
	}
	|
	param_list delim typereference name
	{
	  if ($3 && $4 && emptyParsed)
	    pnerror ("extraneous delimiter in parameter list");
	  addParameter ($3, $4);
	  emptyParsed = false;
	}
	|
	param_list delim
	{
	  if (emptyParsed)
	    pnerror ("extraneous delimiter in parameter list");
	  emptyParsed = true;
	}
	|
	{ if (!currentArity && !currentParameters) emptyParsed = true; }
	;

_opt_capacity:
	{ $$ = NULL; }
	|
	_opt_capacity
	{ currentType = &Net::getCardType (); }
	constraint
	{
	  if (!($$ = $1))
	    $$ = $3;
	  else {
	    if ($$->isDisjoint (*$3)) {
	      pnerror ("ignoring contradictory capacity constraint");
	      delete $1; delete $3;
	      $$ = NULL;
	    }
	    else {
	      $$->restrict (*$3, *currentType); delete $3;
	    }
	  }
	  currentType = NULL;
	}
	;

_opt_const:
	{ $$ = false; }
	|
	_opt_const CONST_
	{ if ($1) pnerror ("ignoring extraneous `const'"); $$ = true; }
	;

place:
	_nopt_place name_ _opt_capacity typedefinition _opt_const
	{
	  currentPlace = NULL;
	  if ((currentType = $4))
	    theNet.addType (*$4);

	  if ($2 && $4) {
	    if (theNet.getPlace ($2)) {
	      thePrinter.printRaw ("ignoring redefinition of place ");
	      thePrinter.printQuoted ($2);
	      thePrinter.finish ();
	      pnerrors++;
	      delete[] $2; delete $3;
	    }
	    else {
	      checkName ($2, ckPlace, "invalid place name ");
	      (currentPlace = &theNet.addPlace ($2, $3, *$4))
		->setConstant ($5);
	    }
	  }
	  else {
	    delete[] $2; delete $3;
	  }
	}
	place_init
	{
	  if (currentPlace && $7)
	    currentPlace->setInitMarking (*$7);
	  else
	    $7->destroy ();
	  currentType = NULL, currentPlace = NULL;
	}
	;

placename:
	name
	{
	  if ($1) {
	    if (!($$ = const_cast<class Place*>(theNet.getPlace ($1)))) {
	      thePrinter.printRaw ("undefined place ");
	      thePrinter.printQuoted ($1);
	      thePrinter.finish ();
	      pnerrors++;
	    }
	  }
	  else
	    $$ = NULL;
	  delete[] $1;
	}
	;

place_init:
	{ $$ = NULL; }
	|
	place_init ':' marking_list
	{
	  if (!($$ = $1))
	    $$ = $3;
	  else {
	    pnerror ("multiple init blocks"); $3->destroy ();
	  }
	}
	;

_opt_colon:
	{ $$ = false; }
	|
	_opt_colon ':'
	{ if ($1) pnerror ("ignoring extraneous `:'"); $$ = true; }
	;

_opt_exclam:
	{ $$ = false; }
	|
	_opt_exclam '!'
	{ if ($1) pnerror ("ignoring extraneous `!'"); $$ = true; }
	;

transition:
	_nopt_trans _opt_colon name_ _opt_exclam
	{
	  if ($3) {
	    if ((currentTransition = $2
		 ? theNet.getCallee ($3, 0)
		 : theNet.getTransition ($3))) {
	      pnwarn ("augmenting transition definition");
	      delete[] $3;
	    }
	    else {
	      if (!$2) checkName ($3, ckTrans, "invalid transition name ");
	      currentTransition = &theNet.addTransition ($3, $2);
	    }
	    if ($4)
	      currentTransition->setPriority (theNet.getMaxPriority ());
	  }
	  else
	    currentTransition = NULL;
	}
	trans
	{
	  if (currentTransition && !$2) {
	    if (const class QuantifierList* q =
		currentTransition->getOutputVariables ()) {
	      for (QuantifierList::const_iterator i = q->begin ();
		   i != q->end (); i++) {
		const class VariableDefinition& v = (*i)->getVariable ();

		if (currentTransition->isInput (&v)) {
		  thePrinter.printRaw ("output variable ");
		  thePrinter.printQuoted (v.getName ());
		  thePrinter.printRaw (" encountered on an input arc");
		  thePrinter.finish ();
		  pnerrors++;
		}
		if (currentTransition->isGate (&v)) {
		  thePrinter.printRaw ("output variable ");
		  thePrinter.printQuoted (v.getName ());
		  thePrinter.printRaw (" encountered on a gate");
		  thePrinter.finish ();
		  pnerrors++;
		}
		if (!currentTransition->isOutput (&v)) {
		  thePrinter.printRaw ("output variable ");
		  thePrinter.printQuoted (v.getName ());
		  thePrinter.printRaw (" not used on output arcs");
		  thePrinter.finish ();
		  pnerrors++;
		}
	      }
	    }
	    if (currentTransition->isInput (0)) {
	      thePrinter.printRaw ("an input arc refers to place contents");
	      thePrinter.finish ();
	      pnerrors++;
	    }
	    if (currentTransition->isGate (0)) {
	      thePrinter.printRaw ("a gate refers to place contents");
	      thePrinter.finish ();
	      pnerrors++;
	    }
	    if (!currentTransition->hasInputs () &&
		currentTransition->hasOutputs ())
	      pnwarn ("transition has output arcs but no inputs");
	    if (!currentTransition->isUnifiable (net->getParent ()
						 ? Transition::uIgnore
						 : Transition::uNormal,
						 thePrinter))
	      pnerror ("transition cannot be unified");
	  }
	  currentTransition = NULL;
	}
	;

transname:
	_nopt_trans name
	{
	  if ($2) {
	    if (!($$ = const_cast<class Transition*>(theNet.getTransition
						     ($2)))) {
	      thePrinter.printRaw ("undefined transition ");
	      thePrinter.printQuoted ($2);
	      thePrinter.finish ();
	      pnerrors++;
	    }
	  }
	  else
	    $$ = NULL;
	  delete[] $2;
	}
	;

trans:
	|
	trans '{' _list_var_expr '}'
	|
	trans IN_
	{ isOutputArc = false; }
	trans_places
	|
	trans OUT_
	{ isOutputArc = true; }
	trans_places
	|
	trans GATE
	{ currentType = &Net::getBoolType (); }
	gate_list
	{ currentType = NULL; }
	|
	trans fairness_strong
	{ currentContextTransition = currentTransition; }
	formula
	{
	  if (currentTransition &&
	      (($4 && $4->getKind () == Expression::eMarking &&
		$4->getType () && $4->getType ()->getKind () == Type::tBool) ||
	      ($4 = ensureBool (ensureExpr ($4))))) {
	    if (!theNet.addConstraint (*$4->cse (), *currentTransition,
				       static_cast<enum Net::CKind>($2)))
	      pnerror ("error in constraint");
	  }
	  else
	    $4->destroy ();
	  currentContextTransition = NULL;
	}
	|
	trans HIDE
	{ currentContextTransition = currentTransition; }
	expr
	{
	  if (currentTransition && ($4 = ensureBool ($4)))
	    currentTransition->addHide (*$4->cse ());
	  else
	    $4->destroy ();
	  currentContextTransition = NULL;
	}
	|
	trans ':' _opt_trans name
	{
	  if (!currentTransition || !$4);
	  else if (const class Transition* t =
		   theNet.getCallee ($4, currentTransition)) {
	    if (!currentTransition->fuse (*t, thePrinter, pnwarnings))
	      pnerrors++;
	  }
	  else {
	    thePrinter.printRaw (":trans ");
	    thePrinter.printQuoted ($4);
	    thePrinter.printRaw (" not found");
	    thePrinter.finish ();
	    pnerrors++;
	  }
	  delete[] $4;
	}
	|
	trans NUMBER
	{
	  if (pnwarnings && currentTransition &&
	      currentTransition->getPriority ()) {
	    thePrinter.printRaw ("redefining priority");
	    thePrinter.finish ();
	  }
	  if (currentTransition) currentTransition->setPriority ($2);
	}
	;

_list_var_expr:
	{ $$ = false; }
	|
	var_expr
	{ $$ = true; }
	|
	_list_var_expr delim var_expr
	{
	  if (!$1)
	    pnerror ("extraneous delimiter before variable definition");
	  $$ = true;
	}
	|
	_list_var_expr delim
	{
	  if (!$1)
	    pnerror ("extraneous delimiter in variable definition list");
	  $$ = false;
	}
	;

var_expr:
	_opt_hide typereference name
	{
	  if ($2 && $3 && currentTransition) {
	    if (currentTransition->getVariable ($3)) {
	      thePrinter.printRaw ("redefinition of variable ");
	      thePrinter.printQuoted ($3);
	      thePrinter.finish ();
	      pnerrors++;
	      delete[] $3;
	    }
	    else {
	      currentTransition->addVariable ($3, *$2, false, $1);
	      checkName ($3, ckVar, "invalid variable name ");
	      if (pnwarnings) {
		if (getConstant ($3) || getConstantFunction ($3)) {
		  thePrinter.printRaw ("Warning:variable ");
		  thePrinter.printQuoted ($3);
		  thePrinter.printRaw (" shadows a constant");
		  thePrinter.finish ();
		}
		else if (getType ($3)) {
		  thePrinter.printRaw ("Warning:variable ");
		  thePrinter.printQuoted ($3);
		  thePrinter.printRaw (" shadows a type name");
		  thePrinter.finish ();
		}
	      }
	    }
	  }
	  else
	    delete[] $3;
	}
	|
	_opt_hide typereference name_ '!'
	{ beginAny ($2, $3, $1); }
	_opt_expr %prec ':'
	{ endAny ($2, $3, $6)->destroy (); }
	|
	_opt_hide function
	{ if ($1) pnerror ("ignoring meaningless `hide'"); }
	;

_opt_hide:
	{ $$ = false; }
	|
	_opt_hide HIDE
	{ $$ = true; if ($1) pnerror ("ignoring extraneous `hide'"); }
	;

trans_places:
	'{' _list_place_marking '}'
	;

_list_place_marking:
	place_marking
	|
	_list_place_marking ';' place_marking
	;

place_marking:
	|
	_opt_place error
	{}
	|
	_opt_place placename ':'
	{
	  currentType = (currentPlace = $2) ? &$2->getType () : NULL;
	}
	marking_list
	{
	  if (!currentPlace || !currentTransition)
	    $5->destroy ();
	  else if ($5) {
	    if (class Arc* arc =
		currentTransition->getArc (isOutputArc, *currentPlace)) {
	      if (pnwarnings) {
		thePrinter.printRaw ("Warning:extending arc ");
		thePrinter.printRaw (isOutputArc
				     ? "to place "
				     : "from place ");
		thePrinter.printQuoted (currentPlace->getName ());
		thePrinter.finish ();
	      }
	      arc->append (*$5);
	    }
	    else if (isOutputArc && currentTransition->getKind ())
	      $5->destroy ();
	    else
	      currentTransition->addArc (*new class Arc
					 (isOutputArc, *$5, *currentPlace));
	  }
	  currentPlace = NULL, currentType = NULL;
	}
	;

_opt_place:
	{ $$ = false; }
	|
	_opt_place PLACE
	{ $$ = true; if ($1) pnerror ("ignoring extraneous `place'"); }
	|
	_opt_place TRANS
	{ $$ = $1; pnerror ("expected `place', got `trans'"); }
	;

_nopt_place:
	PLACE
	|
	_nopt_place PLACE
	{ pnerror ("ignoring extraneous `place'"); }
	|
	_nopt_place TRANS
	{ pnerror ("ignoring extraneous `trans'"); }
	;

_opt_trans:
	{ $$ = false; }
	|
	_opt_place TRANS
	{ $$ = true; if ($1) pnerror ("ignoring extraneous `trans'"); }
	|
	_opt_place PLACE
	{ $$ = $1; pnerror ("expected `trans', got `place'"); }
	;

_nopt_trans:
	TRANS
	|
	_nopt_trans TRANS
	{ pnerror ("ignoring extraneous `trans'"); }
	|
	_nopt_trans PLACE
	{ pnerror ("ignoring extraneous `place' after `trans'"); }
	;

gate_list:
	gate
	|
	gate_list ',' gate
	;

gate:
	expr
	{
	  if (($1 = ensureBool (ensureExpr ($1), true)) && currentTransition) {
	    if ($1->getKind () == Expression::eConstant) {
	      const class Value& v =
		static_cast<const class Constant*>($1)->getValue ();
	      assert (v.getKind () == Value::vLeaf);
	      if (bool (static_cast<const class LeafValue&>(v))) {
		pnwarn ("gate is constantly enabled");
		$1->destroy ();
	      }
	      else {
		pnwarn ("gate is constantly disabled");
		currentTransition->addGate (*$1);
	      }
	    }
	    else if ($1->getKind () == Expression::eUndefined) {
	      if (currentTransition->getKind ())
		pnwarn ("multiple 'fatal' or 'undefined' gates");
	      currentTransition->setAssertion
		(static_cast<class Undefined*>($1)->isFatal ());
	      $1->destroy ();
	    }
	    else
	      currentTransition->addGate (*$1);
	  }
	  else
	    $1->destroy ();
	}
	;

verify:
	REJECT_ expr
	{ if (($2 = ensureBool ($2, true))) theNet.addReject (*$2); }
	|
	DEADLOCK expr
	{ if (($2 = ensureBool ($2, true))) theNet.addDeadlock (*$2); }
	;

prop:
	PROP name ':' expr
	{
	  if (($4 = ensureBool ($4)) && $2) {
	    if (!theNet.addProp ($2, *$4)) {
	      thePrinter.printRaw ("redefinition of state proposition ");
	      thePrinter.printQuoted ($2);
	      thePrinter.finish ();
	      pnerrors++;
	      delete[] $2;
	      $4->destroy ();
	    }
	  }
	  else {
	    delete[] $2; $4->destroy ();
	  }
	}
	;

subnet:
	SUBNET _opt_name '{'
	{
	  if (net) {
	    class Net& child = net->addChild ($2);
	    net = &child;
	  }
	  else
	    delete[] $2;
	}
	net '}'
	{
	  if (net && net->getParent ()) {
	    if (!net->computeInitMarking (thePrinter)) {
	      thePrinter.printRaw ("error in the initial marking");
	      thePrinter.finish ();
	      pnerrors++;
	    }
	    net = net->getParent ();
	  }
	}
	|
	SUBNET _opt_name '{' '}'
	{ delete[] $2; }
	;

fairness:
	fairness_strong
	{ isFairnessStrong = static_cast<enum Net::CKind>($1); }
	fair
	{}
	;

fairness_strong:
	WFAIR
	{ $$ = 0; }
	|
	SFAIR
	{ $$ = 1; }
	|
	ENABLED
	{ $$ = 2; }
	|
	fairness_strong WFAIR
	{ $$ = $1; pnerror ("ignoring extraneous `weakly_fair'"); }
	|
	fairness_strong SFAIR
	{ $$ = $1; pnerror ("ignoring extraneous `strongly_fair'"); }
	|
	fairness_strong ENABLED
	{ $$ = $1; pnerror ("ignoring extraneous `enabled'"); }
	;

fair:
	fair_expr
	|
	fair ',' fair_expr
	;

fair_expr:
	qual_expr
	{
	  if ($1 && !theNet.addConstraint (*$1, isFairnessStrong))
	    pnerror ("error in constraint");
	}
	;

marking:
	formula
	{ $$ = ensureMarking ($1, currentType); }
	;

marking_list:
	marking
	{ $$ = $1; }
	|
	marking_list ',' marking
	{
	  if (($$ = $1) && $3)
	    $$->append (*$3);
	  else if ($3)
	    $$ = $3;
	}
	;

marking_list_:
	formula
	{ $$ = $1; }
	|
	marking_list_ ','
	{ $1 = ensureMarking ($1, currentType); }
	marking
	{
	  assert (!$1 || $1->getKind () == Expression::eMarking);
	  if (($$ = $1) && $4)
	    static_cast<class Marking*>($$)->append (*$4);
	  else if ($4)
	    $$ = $4;
	}
	;

_opt_expr:
	{ $$ = NULL; }
	|
	'(' expr_ ')'
	{ $$ = $2; }
	;

_opt__expr:
	{ $$ = NULL; }
	|
	expr
	{ $$ = $1; }
	|
	NAME ':'
	{ $$ = namedComponent ($1, NULL); }
	|
	NAME ':' expr
	{ $$ = namedComponent ($1, $3); }
	;

struct_expr:
	_opt__expr
	{
	  if (currentContextType && !YYRECOVERING ()) {
	    assert (!currentComponent);

	    switch (currentContextType->getKind ()) {
	    case Type::tVector:
	      {
		const class VectorType* type =
		  static_cast<const class VectorType*>(currentContextType);
		class VectorExpression* v = new class VectorExpression (*type);

		if (!$1) {
		  pnerror ("missing expression for first vector component");
		  v->destroy (), $$ = NULL;
		}
		else if (isCompatible ($1, currentType))
		  $$ = v, v->setComponent (currentComponent, *$1);
		else
		  v->destroy (), $1->destroy (), $$ = NULL;
	      }
	      break;
	    case Type::tBuffer:
	      {
		const class BufferType* type =
		  static_cast<const class BufferType*>(currentContextType);
		class BufferExpression* b = new class BufferExpression (*type);

		if (!$1)
		  $$ = b;
		else if (isCompatible ($1, currentType))
		  $$ = b, b->append (*$1);
		else
		  b->destroy (), $1->destroy (), $$ = NULL;
	      }
	      break;
	    case Type::tStruct:
	      {
		if (!$1 &&
		    !static_cast<const class StructType*>(currentContextType)
		    ->getSize ())
		  $$ = (new class Constant (*new class StructValue
					    (*currentContextType)))->cse ();
		else {
		  class StructExpression* s =
		    new class StructExpression (*currentContextType);

		  if (isCompatible ($1, *s))
		    $$ = s, s->append (*$1), currentType = s->getNextType ();
		  else
		    s->destroy (), $1->destroy (),
		      $$ = NULL, currentType = NULL;
		}
	      }
	      break;
	    default:
	      assert (false);
	    }
	  }
	  else
	    $1->destroy (), $$ = NULL;

	  currentComponent++;
	}
	|
	struct_expr ',' _opt__expr
	{
	  if (($$ = $1) && currentContextType && !YYRECOVERING ()) {
	    assert (currentComponent > 0);
	    switch (currentContextType->getKind ()) {
	    case Type::tVector:
	      {
		assert ($$->getKind () == Expression::eVector);
		class VectorExpression* v =
		  static_cast<class VectorExpression*>($$);
		const class VectorType* type =
		  static_cast<const class VectorType*>(currentContextType);

		if (currentComponent < type->getSize ()) {
		  if (!$3) {
		    thePrinter.printRaw ("missing array item ");
		    thePrinter.print (currentComponent);
		    thePrinter.finish ();
		    pnerrors++;
		    $$->destroy (), $$ = NULL;
		  }
		  else if (isCompatible ($3, currentType))
		    v->setComponent (currentComponent, *$3);
		  else
		    $$->destroy (), $3->destroy (), $$ = NULL;
		}
		else {
		  thePrinter.printRaw ("too many items for array ");
		  printType (*type);
		  thePrinter.finish ();
		  pnerrors++;
		  $$->destroy (), $3->destroy (), $$ = NULL;
		}
	      }
	      break;
	    case Type::tBuffer:
	      {
		assert ($$->getKind () == Expression::eBuffer);
		class BufferExpression* b =
		  static_cast<class BufferExpression*>($$);
		const class BufferType* type =
		  static_cast<const class BufferType*>(currentContextType);

		if (b->hasCapacity ()) {
		  if (!$3 || !b->getSize ()) {
		    thePrinter.printRaw ("missing buffer item ");
		    thePrinter.print ($3 ? currentComponent : 0);
		    thePrinter.finish ();
		    pnerrors++;
		    $$->destroy (), $3->destroy (), $$ = NULL;
		  }
		  else if (isCompatible ($3, currentType))
		    b->append (*$3);
		  else
		    $$->destroy (), $3->destroy (), $$ = NULL;
		}
		else {
		  thePrinter.printRaw ("too many items for buffer ");
		  printType (*type);
		  thePrinter.finish ();
		  pnerrors++;
		  $$->destroy (), $3->destroy (), $$ = NULL;
		}
	      }
	      break;
	    case Type::tStruct:
	      {
		assert ($$->getKind () == Expression::eStruct);
		class StructExpression* s =
		  static_cast<class StructExpression*>($$);

		if (isCompatible ($3, *s))
		  s->append (*$3), currentType = s->getNextType ();
		else
		  $$->destroy (), $3->destroy (),
		    $$ = NULL, currentType = NULL;
	      }
	      break;
	    default:
	      assert (false);
	    }
	  }
	  else
	    $$->destroy (), $3->destroy (), $$ = NULL;

	  currentComponent++;
	}
	;

expr_:
	{ $$ = NULL; pnerror ("missing expression"); }
	|
	expr
	{ $$ = $1; }
	;

expr:
	formula
	{ $$ = ensureExpr ($1); }
	;

quantifier:
	error
	{ $$ = 0; }
	|
	':'
	{ $$ = 1; }
	|
	OR
	{ $$ = 2; }
	|
	AND
	{ $$ = 3; }
	;

formula:
	error
	{ $$ = NULL; }
	|
	name_ NEXT
	{
	  $$ = NULL;
	  if ($1) {
	    if (class Function* f = getFunction ($1)) {
	      if (!($$ = fold (f->expand (NULL)))) {
		thePrinter.printRaw ("could not expand nullary function ");
		thePrinter.printQuoted (f->getName ());
		thePrinter.finish ();
		pnerrors++;
	      }
	    }
	    else {
	      thePrinter.printRaw ("there is no nullary function ");
	      thePrinter.printQuoted ($1);
	      thePrinter.finish ();
	      pnerrors++;
	    }
	  }
	}
	|
	name_ '('
	{
	  class Function* f = NULL;
	  if ($1 && !(f = getFunction ($1))) {
	    thePrinter.printRaw ("there is no function ");
	    thePrinter.printQuoted ($1);
	    thePrinter.finish ();
	    pnerrors++;
	  }
	  delete[] $1;
	  functionStack.push (f);
	}
	arg_list ')'
	{
	  assert (!functionStack.empty ());
	  if (class Function* f = functionStack.top ()) {
	    $$ = NULL;
	    if ($4 && !($$ = fold (f->expand ($4)))) {
	      thePrinter.printRaw ("could not expand function ");
	      thePrinter.printQuoted (f->getName ());
	      thePrinter.finish ();
	      pnerrors++;
	    }
	  }
	  else
	    $$ = NULL;
	  $4->destroy ();
	  functionStack.pop ();
	}
	|
	TRUE_
	{
	  $$ = (new class Constant
		(*new class LeafValue (Net::getBoolType (), true)))->cse ();
	}
	|
	FALSE_
	{
	  $$ = (new class Constant
		(*new class LeafValue (Net::getBoolType (), false)))->cse ();
	}
	|
	_nopt_place placename
	{ $$ = $2 ? (new class PlaceContents (*$2))->cse () : NULL; }
	|
	NAME
	{ $$ = resolveName ($1); }
	|
	NUMBER
	{
	  $$ = (new class Constant
		(*new class LeafValue (Net::getCardType (), $1)))->cse ();
	}
	|
	CHARACTER
	{
	  $$ = (new class Constant
		(*new class LeafValue (Net::getCharType (), $1)))->cse ();
	}
	|
	UNDEFINED
	{ $$ = (new class Undefined (Undefined::fError))->cse (); }
	|
	FATAL
	{ $$ = (new class Undefined (Undefined::fFatalError))->cse (); }
	|
	'{'
	{
	  structLevels.push (StructLevel
			     (currentContextType, currentComponent));

	  if (!currentType) {
	    pnerror ("cannot determine type of structured expression");
	    currentContextType = NULL, currentComponent = 0;
	  }
	  else {
	    assert (!structLevels.empty () || !currentComponent);
	    currentContextType = currentType;
	    currentComponent = 0;

	    switch (currentType->getKind ()) {
	    case Type::tStruct:
	      currentType =
		static_cast<const class StructType*>(currentContextType)
		->getSize ()
		? &(*static_cast<const class StructType*>(currentContextType))
		[currentComponent]
		: NULL;
	      break;
	    case Type::tVector:
	      currentType =
		&static_cast<const class VectorType*>(currentContextType)
		->getItemType ();

	      if (pnwarnings) {
		const class Type& indexType =
		  static_cast<const class VectorType*>(currentContextType)
		  ->getIndexType ();

		switch (indexType.getKind ()) {
		case Type::tInt:
		case Type::tCard:
		case Type::tBool:
		case Type::tChar:
		case Type::tEnum:
		case Type::tId:
		  if (const class Constraint* constraint =
		      indexType.getConstraint ()) {
		    if (constraint->size () <= 1)
		      break;
		  }
		  else
		    break;
		default:
		  pnwarn ("initializing an array indexed by something else"
			  " than a simple continuous type");
		}
	      }

	      break;
	    case Type::tBuffer:
	      currentType =
		&static_cast<const class BufferType*>(currentContextType)
		->getItemType ();

	      break;
	    default:
	      pnerror ("cannot determine type of structured expression");
	      currentType = currentContextType = NULL;
	    }
	  }
	}
	struct_expr
	'}'
	{
	  $$ = $3;
	  if (currentContextType) {
	    if ($$) {
	      switch ($$->getKind ()) {
	      case Expression::eStruct:
		{
		  class StructExpression* s =
		    static_cast<class StructExpression*>($$);
		  if (s->getNextType ()) {
		    thePrinter.printRaw ("structure ");
		    printType (*currentContextType);
		    thePrinter.printRaw (" not fully initialized");
		    thePrinter.finish ();
		    pnerrors++;
		    $$->destroy (), $$ = NULL;
		  }
		}
		break;

	      case Expression::eConstant:
	      case Expression::eBuffer:
		break;

	      case Expression::eVector:
		{
		  card_t diff =
		    static_cast<const class VectorType*>(currentContextType)
		    ->getSize () - currentComponent;

		  if (diff) {
		    thePrinter.print (diff);
		    thePrinter.printRaw (diff == 1
					 ? " element of array "
					 : " elements of array ");
		    printType (*currentContextType);
		    thePrinter.printRaw (" left uninitialized");
		    thePrinter.finish ();
		    pnerrors++;

		    $$->destroy (), $$ = NULL;
		  }
		}
		break;

	      default:
		assert (false);
	      }
	    }

	    StructLevel p = structLevels.top ();
	    currentType = currentContextType;
	    currentContextType = p.first;
	    currentComponent = p.second;
	  }
	  else
	    currentComponent = 0;

	  structLevels.pop ();

	  if ($$)
	    $$ = fold ($$);
	}
	|
	IS typereference
	{ pushTypeContext ($2); }
	formula %prec ATOM_
	{
	  if (($$ = $4) && $$->getKind () == Expression::eMarking &&
	      $$->getType () == currentType);
	  else if (($$ = noMarking ($$)) && currentType) {
	    if (!$$->getType ()) {
	      pnerror ("expression to cast has no type");
	      $$->destroy (), $$ = NULL;
	    }
	    else
	      makeCompatible ($$, *currentType, true);
	  }
	  popTypeContext ();
	}
	|
	name_ '='
	{
	  StructLevel p (currentContextType, currentComponent);
	  structLevels.push (p);
	  currentContextType = currentType;
	  currentType = NULL, currentComponent = 0;

	  if ($1 && currentContextType &&
	      currentContextType->getKind () == Type::tUnion) {
	    const class UnionType* u =
	      static_cast<const class UnionType*>(currentContextType);

	    if ((currentType = (*u)[$1]))
	      currentComponent = u->getIndex ($1);
	    else {
	      thePrinter.printRaw ("type ");
	      printType (*u);
	      thePrinter.printRaw (" does not have component ");
	      thePrinter.printQuoted ($1);
	      thePrinter.finish ();
	      pnerrors++;
	    }
	  }
	  else if ($1)
	    pnerror ("cannot assign the component of a non-union type");

	  delete[] $1;
	}
	formula %prec UNTIL
	{
	  if (($4 = ensureExpr ($4)) && currentType) {
	    const class UnionType* u =
	      static_cast<const class UnionType*>(currentContextType);

	    if (isCompatible ($4, currentType))
	      $$ = fold (new class UnionExpression (*u, *$4,
						    currentComponent));
	    else {
	      thePrinter.printRaw ("type mismatch for ");
	      printType (*u);
	      thePrinter.printRaw (" component ");
	      thePrinter.printQuoted (u->getComponentName (currentComponent));
	      thePrinter.finish ();
	      pnerrors++;

	      $4->destroy (), $$ = NULL;
	    }
	  }
	  else
	    $4->destroy (), $$ = NULL;

	  assert (!structLevels.empty ());
	  StructLevel p = structLevels.top ();
	  currentType = currentContextType;
	  currentContextType = p.first;
	  currentComponent = p.second;
	  structLevels.pop ();

	  if ($$)
	    $$ = fold ($$);
	}
	|
	formula IS name
	{
	  if (($1 = ensureExpr ($1)) && $3) {
	    if ($1->getType () && $1->getType ()->getKind () == Type::tUnion) {
	      const class UnionType* u =
		static_cast<const class UnionType*>($1->getType ());

	      if ((*u)[$3])
		$$ = fold (new class UnionTypeExpression (*$1,
							  u->getIndex ($3)));
	      else {
		thePrinter.printRaw ("type ");
		printType (*u);
		thePrinter.printRaw (" does not have component ");
		thePrinter.printQuoted ($3);
		thePrinter.finish ();
		pnerrors++;

		$1->destroy (), $$ = NULL;
	      }
	    }
	    else {
	      pnerror ("cannot determine the component of a non-union value");
	      $1->destroy (), $$ = NULL;
	    }
	  }
	  else
	    $1->destroy (), $$ = NULL;

	  delete[] $3;

	  if ($$)
	    $$ = fold ($$);
	}
	|
	ATOM_ formula
	{ if (($$ = noMarking ($2))) ($$ = $$->cse ())->setAtomic (true); }
	|
	typereference '!'
	{ beginAny ($1, NULL, false); }
	_opt_expr %prec ':'
	{ $$ = endAny ($1, NULL, $4); }
	|
	typereference name_ '!'
	{ beginAny ($1, $2, false); }
	_opt_expr %prec ':'
	{ $$ = endAny ($1, $2, $5); }
	|
	formula '<' formula
	{ $$ = newRelopExpression (false, $1, $3); }
	|
	formula EQ formula
	{ $$ = newRelopExpression (true, $1, $3); }
	|
	formula '>' formula
	{ $$ = newRelopExpression (false, $3, $1); }
	|
	formula GE formula
	{ $$ = newNotExpression (newRelopExpression (false, $1, $3)); }
	|
	formula NE formula
	{ $$ = newNotExpression (newRelopExpression (true, $1, $3)); }
	|
	formula LE formula
	{ $$ = newNotExpression (newRelopExpression (false, $3, $1)); }
	|
	formula '+' formula
	{
	  if ($1 && $1->getType () &&
	      $1->getType ()->getKind () == Type::tBuffer) {
	    const class BufferType* type =
	      static_cast<const class BufferType*>($1->getType ());
	    if (($1 = ensureExpr ($1)) && ($3 = ensureExpr ($3)) &&
		isCompatible ($3, &type->getItemType ())) {
	      if ($1->getKind () == Expression::eBufferIndex) {
		class BufferIndex* b = static_cast<class BufferIndex*>($1);
		$$ = fold (new class BufferWrite (b->getBuffer (), *$3,
						  &b->getIndex ()));
		b->destroy ();
	      }
	      else
		$$ = fold (new class BufferWrite (*$1, *$3, NULL));
	    }
	    else
	      $1->destroy (), $3->destroy (), $$ = NULL;
	  }
	  else
	    $$ = newBinopExpression (BinopExpression::bPlus, $1, $3);
	}
	|
	formula '-' formula
	{
	  if (($1 = ensureExpr ($1)) && $1->getType () &&
	      $1->getType ()->getKind () == Type::tBuffer) {
	    if (($$ = ensureBuffer ($1))) {
	      class Expression* i = 0;
	      if ($$->getKind () == Expression::eBufferIndex) {
		class BufferIndex* b = static_cast<class BufferIndex*>($$);
		$$ = &b->getBuffer ();
		i = &b->getIndex ();
		b->destroy ();
	      }
	      $$ = fold (new class BufferRemove (*$$, ensureCard ($3), i));
	    }
	  }
	  else
	    $$ = newBinopExpression (BinopExpression::bMinus, $1, $3);
	}
	|
	formula '/' formula
	{ $$ = newBinopExpression (BinopExpression::bDiv, $1, $3); }
	|
	formula '*' formula
	{ $$ = newBinopExpression (BinopExpression::bMul, $1, $3); }
	|
	formula '%' formula
	{ $$ = newBinopExpression (BinopExpression::bMod, $1, $3); }
	|
	'~' formula
	{
	  if (($$ = ensureIntCard ($2)))
	    $$ = fold (new class UnopExpression (UnopExpression::uNot, *$$));
	}
	|
	'#' typereference %prec '!'
	{
	  if ($2) {
	    card_t num = $2->getNumValues ();
	    assert (num > 0);
	    if (num == CARD_T_MAX) {
	      pnerror ("type has too many values to count");
	      $$ = NULL;
	    }
	    else
	      $$ = (new class Constant (*new class LeafValue
					(Net::getCardType (), num)))->cse ();
	  }
	  else
	    $$ = NULL;
	}
	|
	'<' typereference %prec '!'
	{
	  if ($2 && isOrdered (*$2))
	    $$ = (new class Constant ($2->getFirstValue ()))->cse ();
	  else
	    $$ = NULL;
	}
	|
	'>' typereference %prec '!'
	{
	  if ($2 && isOrdered (*$2))
	    $$ = (new class Constant ($2->getLastValue ()))->cse ();
	  else
	    $$ = NULL;
	}
	|
	'|' formula %prec '!'
	{
	  if (($$ = ensureOrdered ($2)))
	    $$ = fold (new class UnopExpression (UnopExpression::uPred, *$$));
	}
	|
	'+' formula %prec '!'
	{
	  if (($$ = ensureOrdered ($2)))
	    $$ = fold (new class UnopExpression (UnopExpression::uSucc, *$$));
	}
	|
	'-' formula %prec '!'
	{
	  if (($2 = ensureExpr ($2)) && $2->getType () &&
	      $2->getType ()->getKind () == Type::tBuffer) {
	    if (($$ = ensureBuffer ($2))) {
	      class Expression* i = 0;
	      if ($$->getKind () == Expression::eBufferIndex) {
		class BufferIndex* b = static_cast<class BufferIndex*>($$);
		$$ = &b->getBuffer ();
		i = &b->getIndex ();
		b->destroy ();
	      }
	      $$ = fold (new class BufferRemove (*$$, 0, i));
	    }
	  }
	  else if (($$ = ensureIntCard ($2)))
	    $$ = fold (new class UnopExpression (UnopExpression::uNeg, *$$));
	}
	|
	'*' formula %prec '!'
	{ $$ = newBufferUnop (BufferUnop::bPeek, $2); }
	|
	'/' formula %prec '!'
	{ $$ = newBufferUnop (BufferUnop::bUsed, $2); }
	|
	'%' formula %prec '!'
	{ $$ = newBufferUnop (BufferUnop::bFree, $2); }
	|
	formula ROL formula
	{ $$ = newBinopExpression (BinopExpression::bRol, $1, $3); }
	|
	formula ROR formula
	{ $$ = newBinopExpression (BinopExpression::bRor, $1, $3); }
	|
	formula '&' formula
	{ $$ = newBinopExpression (BinopExpression::bAnd, $1, $3); }
	|
	formula '^' formula
	{ $$ = newBinopExpression (BinopExpression::bXor, $1, $3); }
	|
	formula '|' formula
	{ $$ = newBinopExpression (BinopExpression::bOr, $1, $3); }
	|
	formula '?' colon_formula
	{
	  $1 = noMarking ($1);
	  if ($1 && $3) {
	    if (!$1->getType ()) {
	      pnerror ("left-hand-side of ? has no type");
	      $1->destroy (); $3->destroy (); $$ = NULL;
	    }
	    else if ($1->getType ()->getNumValues () != $3->size ()) {
	      thePrinter.printRaw ("left-hand-side of ? has ");
	      thePrinter.print ($1->getType ()->getNumValues ());
	      thePrinter.printRaw ("!=");
	      thePrinter.print ($3->size ());
	      thePrinter.printRaw (" possible values");
	      thePrinter.finish ();
	      pnerrors++;
	      $1->destroy (); $3->destroy (); $$ = NULL;
	    }
	    else if ($1->getType ()->getNumValues () == 1) {
	      pnwarn ("left-hand-side of ? has only one possible value");
	      $$ = (*$3)[0].copy (); $1->destroy (); $3->destroy ();
	    }
	    else
	      $$ = fold (new class IfThenElse (*$1, *$3));
	  }
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	formula '.' '{' '['
	{
	  if (($1 = ensureExpr ($1))) {
	    if (!$1->getType () ||
		$1->getType ()->getKind () != Type::tVector) {
	      pnerror ("left-hand-side of .{[index]expr} is not an array");
	      $1->destroy (), $1 = NULL;
	    }
	    else
	      pushTypeContext (&static_cast<const class VectorType*>
			       ($1->getType ())->getIndexType ());
	  }
	}
	expr_ ']'
	{
	  if ($1)
	    currentType = &static_cast<const class VectorType*>($1->getType ())
	      ->getItemType ();
	}
	expr_ '}'
	{
	  if ($1) {
	    currentType = $1->getType ();
	    const class VectorType& type =
	      *static_cast<const class VectorType*>(currentType);

	    if (makeCompatible ($6, type.getIndexType ()) &&
		makeCompatible ($9, type.getItemType ()))
	      $$ = fold (new class VectorAssign (*$1, *$6, *$9));
	    else
	      $6->destroy (), $9->destroy (), $1->destroy (), $$ = NULL;
	    popTypeContext ();
	  }
	  else
	    $6->destroy (), $9->destroy (), $$ = NULL;
	}
	|
	formula '.' '{' name_
	{
	  if (($1 = ensureExpr ($1)) && $4) {
	    if (!$1->getType () ||
		$1->getType ()->getKind () != Type::tStruct) {
	      pnerror ("left-hand-side of .{comp expr} is not a struct");
	      $1->destroy (), $1 = NULL;
	    }
	    const class StructType& type =
	      *static_cast<const class StructType*>($1->getType ());
	    if (const class Type* ct = type[$4])
	      pushTypeContext (ct);
	    else {
	      thePrinter.printRaw ("type ");
	      printType (type);
	      thePrinter.printRaw (" does not have component ");
	      thePrinter.printQuoted ($4);
	      thePrinter.finish ();
	      pnerrors++;
	      $1->destroy (), $1 = NULL;
	    }
	  }
	  else
	    $1->destroy (), $1 = NULL;
	}
	expr_ '}'
	{
	  if ($1) {
	    const class StructType& type =
	      *static_cast<const class StructType*>($1->getType ());
	    currentType = type[$4];
	    if (makeCompatible ($6, *currentType))
	      $$ =
		fold (new class StructAssign (*$1, type.getIndex ($4), *$6));
	    else
	      $6->destroy (), $1->destroy (), $$ = NULL;
	    popTypeContext ();
	  }
	  else
	    $6->destroy (), $$ = NULL;
	  delete[] $4;
	}
	|
	formula '.' name_
	{
	  $1 = ensureExpr ($1);
	  if ($1 && $3) {
	    if ($1->getType () &&
		$1->getType ()->getKind () == Type::tStruct) {
	      const class StructType *s =
		static_cast<const class StructType*>($1->getType ());
	      if ((*s)[$3])
		$$ = fold (new class StructComponent (*$1, s->getIndex ($3)));
	      else {
		thePrinter.printRaw ("type ");
		printType (*s);
		thePrinter.printRaw (" does not have component ");
		thePrinter.printQuoted ($3);
		thePrinter.finish ();
		pnerrors++;

		$1->destroy (), $$ = NULL;
	      }
	    }
	    else if ($1->getType () &&
		     $1->getType ()->getKind () == Type::tUnion) {
	      const class UnionType *u =
		static_cast<const class UnionType*>($1->getType ());
	      if ((*u)[$3])
		$$ = fold (new class UnionComponent (*$1, u->getIndex ($3)));
	      else {
		thePrinter.printRaw ("type ");
		printType (*u);
		thePrinter.printRaw (" does not have component ");
		thePrinter.printQuoted ($3);
		thePrinter.finish ();
		pnerrors++;

		$1->destroy (), $$ = NULL;
	      }
	    }
	    else {
	      pnerror ("only unions and structs have components");
	      $1->destroy (), $$ = NULL;
	    }
	  }
	  else
	    $1->destroy (), $$ = NULL;

	  delete[] $3;
	}
	|
	formula '['
	{
	  if (($1 = ensureExpr ($1)) && $1->getType ()) {
	    switch ($1->getType ()->getKind ()) {
	    case Type::tVector:
	      {
		const class VectorType* type =
		  static_cast<const class VectorType*>($1->getType ());
		pushTypeContext (&type->getIndexType ());
	      }
	      break;
	    case Type::tBuffer:
	      if ($1->getKind () == Expression::eBufferIndex)
		pnerror ("a buffer index has already been specified");
	      break;
	    default:
	      pnerror ("only vector-typed expressions can be indexed");
	      $1->destroy (), $1 = NULL;
	    }
	  }
	  else if ($1) {
	    pnerror ("no type for indexed expression");
	    $1->destroy (), $1 = NULL;
	  }
	}
	expr ']'
	{
	  if ($1) {
	    switch ($1->getType ()->getKind ()) {
	    case Type::tVector:
	      {
		const class VectorType* type =
		  static_cast<const class VectorType*>($1->getType ());
		popTypeContext ();

		if (isCompatible ($4, &type->getIndexType ()))
		  $$ = fold (new class VectorIndex (*$1, *$4));
		else
		  $$ = NULL, $1->destroy (), $4->destroy ();
	      }
	      break;
	    case Type::tBuffer:
	      $4 = ensureCard ($4);
	      if ($1->getKind () == Expression::eBufferIndex)
		$4->destroy (), $$ = $1;
	      else
		$$ = $4 ? (new class BufferIndex (*$1, *$4))->cse () : $1;
	      break;
	    default:
	      assert (false);
	    }
	  }
	  else
	    $$ = NULL, $4->destroy ();
	}
	|
	'!' formula
	{ $$ = newNotExpression ($2); }
	|
	formula AND formula
	{
	  $1 = ensureBool ($1); $3 = ensureBool ($3, true);
	  if ($1 && $3)
	    $$ = newBooleanBinop (true, $1, $3);
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	formula OR formula
	{
	  $1 = ensureBool ($1); $3 = ensureBool ($3, true);
	  if ($1 && $3)
	    $$ = newBooleanBinop (false, $1, $3);
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	formula IMPL formula
	{
	  $1 = ensureBool ($1); $3 = ensureBool ($3, true);
	  if ($1 && $3)
	    $$ = newBooleanBinop (false, newNotExpression ($1), $3);
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	formula XOR formula
	{
	  $$ = newNotExpression
	    (newRelopExpression (true, ensureBool ($1), ensureBool ($3)));
	}
	|
	formula EQUIV formula
	{ $$ = newRelopExpression (true, ensureBool ($1), ensureBool ($3)); }
	|
	FINALLY formula
	{
	  $2 = ensureBool ($2);
	  $$ = $2
	    ? (new class TemporalUnop (TemporalUnop::Finally, *$2))->cse ()
	    : NULL;
	}
	|
	GLOBALLY formula
	{
	  $2 = ensureBool ($2);
	  $$ = $2
	    ? (new class TemporalUnop (TemporalUnop::Globally, *$2))->cse ()
	    : NULL;
	}
	|
	NEXT formula
	{
	  $2 = ensureBool ($2);
	  $$ = $2
	    ? (new class TemporalUnop (TemporalUnop::Next, *$2))->cse ()
	    : NULL;
	}
	|
	formula UNTIL formula
	{
	  $1 = ensureBool ($1); $3 = ensureBool ($3);
	  if ($1 && $3)
	    $$ = (new class TemporalBinop
		  (TemporalBinop::Until, *$1, *$3))->cse ();
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	formula RELEASE formula
	{
	  $1 = ensureBool ($1); $3 = ensureBool ($3);
	  if ($1 && $3)
	    $$ = (new class TemporalBinop
		  (TemporalBinop::Release, *$1, *$3))->cse ();
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	EMPTY
	{ $$ = (new class EmptySet)->cse (); }
	|
	formula SUBSET formula
	{ $$ = newSetExpression (SetExpression::sSubset, $1, $3); }
	|
	formula INTERSECT formula
	{ $$ = newSetExpression (SetExpression::sIntersection, $1, $3); }
	|
	formula MINUS formula
	{ $$ = newSetExpression (SetExpression::sMinus, $1, $3); }
	|
	formula UNION formula
	{ $$ = newSetExpression (SetExpression::sUnion, $1, $3); }
	|
	formula EQUALS formula
	{ $$ = newSetExpression (SetExpression::sEquals, $1, $3); }
	|
	CARDINALITY formula
	{ $$ = newCardinalityExpression (CardinalityExpression::cCard, $2); }
	|
	MIN_ formula
	{ $$ = newCardinalityExpression (CardinalityExpression::cMin, $2); }
	|
	MAX_ formula
	{ $$ = newCardinalityExpression (CardinalityExpression::cMax, $2); }
	|
	'(' marking_list_ ')'
	{ $$ = $2; }
	|
	formula '#' formula
	{
	  $1 = ensureCard ($1);
	  class Marking* m = ensureMarking ($3, currentType);

	  if (($$ = m) && $1) {
	    if ($1->getKind () == Expression::eConstant) {
	      const class Value& v =
		static_cast<class Constant*>($1)->getValue ();
	      assert (v.getKind () == Value::vLeaf);
	      card_t mult = card_t (static_cast<const class LeafValue&>(v));
	      if (!mult) {
		pnerror ("zero multiplicity");
		$1->destroy (), m->destroy ();
		$$ = m = NULL;
	      }
	      else if (mult == 1)
		$1->destroy ();
	      else {
		for (;;) {
		  if (class Expression* c = m->getMultiplicity ()) {
		    class Expression* card =
		      fold (new class BinopExpression (BinopExpression::bMul,
						       *$1, *c));
		    m->setMultiplicity (card);
		  }
		  else
		    m->setMultiplicity ($1);

		  if ((m = const_cast<class Marking*>(m->getNext ())))
		    $1 = (new class Constant
			  (*new class LeafValue
			   (*$1->getType (), mult)))->cse ();
		  else
		    break;
		}
	      }
	    }
	    else {
	      if (m->getNext ())
		$$ = m = static_cast<class Marking*>
		  ((new class Marking (m->getPlace (), m))->cse ());

	      class Expression* card = $1;

	      if (class Expression* c = m->getMultiplicity ())
		card = fold
		  (new class BinopExpression (BinopExpression::bMul, *$1, *c));
	      m->setMultiplicity (card);
	    }
	  }
	  else
	    $1->destroy ();
	}
	|
	typereference name_
	{ beginQuantifier ($2, $1); }
	_opt_expr
	{ continueQuantifier (ASSERT1 ($1)); }
	quantifier formula %prec ':'
	{ $$ = endQuantifier ($2, $1, $4, $6, $7, false); }
	|
	'.' name_ _opt_name
	{ $$ = quantifierVariable ($2, $3, false); }
	|
	':' name_ _opt_name
	{ $$ = quantifierVariable ($2, $3, true); }
	|
	SUBSET name '{' marking_list_ '}'
	{
	  if ($4 && $4->getKind () == Expression::eEmptySet) {
	    pnwarn ("any subset of an empty set is an empty set");
	    delete[] $2, $2 = NULL;
	  }
	  else if (($4 = ensureSet ($4)) && $2 && $4->getType ()) {
	    checkIterator ($2);
	    currentVariables.push (new class VariableDefinition
				   ($2, *$4->getType (), false));
	  }
	  else
	    delete[] $2, $2 = NULL;
	}
	formula %prec CARDINALITY
	{
	  $7 = ensureBool (ensureExpr ($7));
	  if (($$ = $4) && $7 && $2) {
	    $7 = $7->cse ();
	    if (pnwarnings) {
	      class VariableSet vars;
	      vars.insert (*currentVariables.top ());
	      if (!$7->depends (vars, false)) {
		thePrinter.printRaw ("Warning:subset is independent of ");
		thePrinter.printQuoted (currentVariables.top ()->getName ());
		thePrinter.finish ();
	      }
	    }
	    if ($7->getKind () == Expression::eConstant) {
	      const class Value& v =
		static_cast<class Constant*>($7)->getValue ();
	      assert (v.getKind () == Value::vLeaf);
	      bool result = bool (static_cast<const class LeafValue&>(v));
	      $7->destroy ();
	      pnwarn (result
		      ? "subset criterion always passes"
		      : "subset criterion always fails");
	      if (!result)
		$$->destroy (), $$ = (new class EmptySet)->cse ();
	    }
	    else
	      $$ = (new class Submarking
		    (*currentVariables.top (), *$4, *$7))->cse ();
	  }
	  else
	    $7->destroy ();
	  if ($2)
	    currentVariables.pop ();
	}
	|
	MAP name '{' marking_list_ '}'
	{
	  if ($4 && $4->getKind () == Expression::eEmptySet) {
	    pnwarn ("any subset of an empty set is an empty set");
	    delete[] $2, $2 = NULL;
	  }
	  else if (($4 = ensureSet ($4)) && $2 && $4->getType ()) {
	    checkIterator ($2);
	    currentVariables.push (new class VariableDefinition
				   ($2, *$4->getType (), false));
	  }
	  else
	    delete[] $2, $2 = NULL;
	}
	formula %prec MAP
	{
	  $7 = ensureExpr ($7);
	  if (($$ = $4) && $2 && $7) {
	    $7 = $7->cse ();
	    if (pnwarnings) {
	      class VariableSet vars;
	      vars.insert (*currentVariables.top ());
	      if (!$7->depends (vars, false)) {
		thePrinter.printRaw ("Warning:mapping is independent of ");
		thePrinter.printQuoted (currentVariables.top ()->getName ());
		thePrinter.finish ();
	      }
	      else if ($7->getKind () == Expression::eVariable &&
		       &static_cast<class Variable*>($7)->getVariable () ==
		       currentVariables.top ()) {
		thePrinter.printRaw ("Warning:mapping has no effect");
		thePrinter.finish ();
	      }
	    }

	    if ($7->getKind () == Expression::eVariable &&
		&static_cast<class Variable*>($7)->getVariable () ==
		currentVariables.top ()) {
	      currentVariables.pop ();
	      delete &static_cast<class Variable*>($7)->getVariable ();
	      $2 = 0;
	      $7->destroy ();
	      $$ = $4;
	    }
	    else
	      $$ = (new class Mapping
		    (*currentVariables.top (), NULL, *$4, *$7))->cse ();
	  }
	  else
	    $7->destroy ();
	  if ($2)
	    currentVariables.pop ();
	}
	|
	MAP name '#' name '{' marking_list_ '}'
	{
	  if ($6 && $6->getKind () == Expression::eEmptySet) {
	    pnwarn ("any subset of an empty set is an empty set");
	    delete[] $2, $2 = NULL;
	    delete[] $4, $4 = NULL;
	  }
	  else if (($6 = ensureSet ($6)) && $2 && $4 && $6->getType ()) {
	    checkIterator ($2); checkIterator ($4);
	    currentVariables.push (new class VariableDefinition
				   ($2, Net::getCardType (), false));
	    currentVariables.push (new class VariableDefinition
				   ($4, *$6->getType (), false));
	  }
	  else {
	    delete[] $2, $2 = NULL;
	    delete[] $4, $4 = NULL;
	  }
	}
	formula %prec MAP
	{
	  $9 = $9 ? ensureMarking ($9, $9->getType ()) : 0;
	  $$ = $6;
	  if ($2 && $4) {
	    class VariableDefinition* token = currentVariables.top ();
	    currentVariables.pop ();
	    class VariableDefinition* mult = currentVariables.top ();
	    currentVariables.pop ();
	    if ($$ && $9) {
	      $9 = $9->cse ();
	      if (pnwarnings) {
		class VariableSet vars;
		vars.insert (*token);
		vars.insert (*mult);
		if (!$9->depends (vars, false)) {
		  thePrinter.printRaw ("Warning:mapping depends neither on ");
		  thePrinter.printQuoted (token->getName ());
		  thePrinter.printRaw (" nor ");
		  thePrinter.printQuoted (mult->getName ());
		  thePrinter.finish ();
		}
	      }
	      $$ = (new class Mapping
		    (*token, mult, *$6, *$9))->cse ();
	    }
	    else
	      $9->destroy ();
	  }
	  else
	    $9->destroy ();
	}
	;

arg_list:
	{
	  if (class Function* f = functionStack.top ()) {
	    if (!f->getArity ())
	      $$ = new class ExpressionList;
	    else {
	      thePrinter.printRaw ("function ");
	      thePrinter.printQuoted (f->getName ());
	      thePrinter.printRaw (" expects ");
	      thePrinter.print (f->getArity ());
	      thePrinter.printRaw (", not zero arguments");
	      thePrinter.finish ();
	      pnerrors++;
	      $$ = NULL;
	    }
	  }
	  else
	    $$ = NULL;
	}
	|
	{
	  const class Type* type = NULL;

	  if (class Function* f = functionStack.top ()) {
	    if (f->getArity ())
	      type = &(*f)[0].getType ();
	    else {
	      thePrinter.printRaw ("function ");
	      thePrinter.printQuoted (f->getName ());
	      thePrinter.printRaw (" takes no arguments");
	      thePrinter.finish ();
	      pnerrors++;
	    }
	  }

	  pushTypeContext (type);
	}
	formula
	{
	  if (currentType &&
	      isCompatible (($2 = noMarking ($2)), currentType)) {
	    if (functionStack.top ())
	      ($$ = new class ExpressionList)->append (*$2);
	    else {
	      $2->destroy (); $$ = NULL;
	    }
	  }
	  else {
	    $2->destroy (); $$ = NULL;
	  }
	  popTypeContext ();
	}
	|
	arg_list ','
	{
	  const class Type* type = NULL;

	  if (class Function* f = functionStack.top ()) {
	    if ($1) {
	      if (f->getArity () > $1->size ())
		type = &(*f)[$1->size ()].getType ();
	      else {
		thePrinter.printRaw ("function ");
		thePrinter.printQuoted (f->getName ());
		thePrinter.printRaw (" takes only ");
		thePrinter.print (f->getArity ());
		thePrinter.printRaw (" arguments");
		thePrinter.finish ();
		pnerrors++;
	      }
	    }
	  }

	  pushTypeContext (type);
	}
	formula
	{
	  if (($$ = $1) && currentType &&
	      isCompatible (($4 = noMarking ($4)), currentType)) {
	    if (functionStack.top ())
	      $$->append (*$4);
	    else {
	      $$->destroy (); $4->destroy (); $$ = NULL;
	    }
	  }
	  else {
	    $$->destroy (); $4->destroy (); $$ = NULL;
	  }
	  popTypeContext ();
	}
	|
	arg_list ','
	{ if (($$ = $1)) pnerror ("extraneous comma in argument list"); }
	;


colon_formula:
	formula %prec ':'
	{
	  if ($1)
	    ($$ = new class ExpressionList)->prepend (*$1);
	  else
	    $$ = NULL;
	}
	|
	colon_formula ':' formula
	{
	  if (($$ = $1) && $3) {
	    if ($$->getType () && $3->getType () &&
		$$->getType () != $3->getType ()) {
	      pnerror ("incompatible types in ?: expression");
	      $$->destroy (), $3->destroy (), $$ = NULL;
	    }
	    else if (($3 = $$->isSet ()
		      ? ensureMarking ($3, $$->getType ()
				       ? $$->getType ()
				       : $3->getType ())
		      : noMarking ($3)))
	      $$->prepend (*$3);
	    else
	      $$->destroy (), $3->destroy (), $$ = NULL;
	  }
	  else
	    $$->destroy (), $3->destroy (), $$ = NULL;
	}
	;

qual_expr:
	typereference name_
	{ beginQuantifier ($2, $1); }
	_opt_expr
	{ continueQuantifier (ASSERT1 ($1)); }
	quantifier qual_expr %prec ':'
	{ $$ = endQuantifier ($2, $1, $4, $6, $7, true); }
	|
	transname ':'
	{ currentContextTransition = $1; }
	formula
	{
	  $4 = ensureBool (ensureExpr ($4));
	  if ($1 && $4)
	    $$ = (new class TransitionQualifier (*$1, *$4->cse ()))->cse ();
	  else
	    $4->destroy (), $$ = NULL;
	  currentContextTransition = NULL;
	}
	|
	transname
	{
	  $$ = $1
	    ? (new class TransitionQualifier
	       (*$1, *(new class Constant
		       (*new class LeafValue
			(Net::getBoolType (), true)))->cse ()))->cse ()
	    : NULL;
	}
	|
	'(' qual_expr ')'
	{ $$ = $2; }
	|
	'!' qual_expr
	{ $$ = newNotExpression ($2); }
	|
	qual_expr AND qual_expr
	{
	  if ($1 && $3)
	    $$ = newBooleanBinop (true, $1, $3);
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	qual_expr OR qual_expr
	{
	  if ($1 && $3)
	    $$ = newBooleanBinop (false, $1, $3);
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	qual_expr IMPL qual_expr
	{
	  if ($1 && $3)
	    $$ = newBooleanBinop (false, newNotExpression ($1), $3);
	  else {
	    $1->destroy (); $3->destroy (); $$ = NULL;
	  }
	}
	|
	qual_expr XOR qual_expr
	{ $$ = newNotExpression (newRelopExpression (true, $1, $3)); }
	|
	qual_expr EQUIV qual_expr
	{ $$ = newRelopExpression (true, $1, $3); }
	;

%%

static void
checkIterator (const char* name)
{
  checkName (name, ckIter, "invalid iterator variable name ");
}

static class Type*
copyType (class Type* type)
{
  if (type->getName () || Net::isPredefinedType (*type)) {
    if (!theNet.hasType (*type)) theNet.addType (*type, 0);
    return type->copy ();
  }
  else
    return type;
}

static void
deleteType (class Type* type)
{
  if (type && !type->getName () && !Net::isPredefinedType (*type) &&
      !theNet.hasType (*type))
    delete type;
}

static void
printType (const class Type& type)
{
  if (const char* name = type.getName ())
    thePrinter.print (name);
  else
    type.display (thePrinter);
}

static void pushTypeContext (const class Type* type)
{
  StructLevel p (currentContextType, currentComponent);
  structLevels.push (p);
  StructLevel q (currentType, 0);
  structLevels.push (q);

  currentType = type;
  currentContextType = NULL, currentComponent = 0;
}

static void popTypeContext ()
{
  StructLevel p = structLevels.top ();
  structLevels.pop ();
  currentType = p.first;
  assert (!p.second);
  p = structLevels.top ();
  structLevels.pop ();
  currentContextType = p.first;
  currentComponent = p.second;
}

static const class VariableDefinition*
getParameter (char* name)
{
  for (unsigned i = 0; i < currentArity; i++) {
    if (!strcmp (currentParameters[i]->getName (), name))
      return currentParameters[i];
  }

  return NULL;
}

static void
addParameter (const class Type* type, char* name)
{
  if (!type || !name) {
    delete[] name;
    return;
  }

  if (getParameter (name)) {
    thePrinter.printRaw ("ignoring redefinition of parameter ");
    thePrinter.printQuoted (name);
    thePrinter.finish ();
    pnerrors++;
    delete[] name;
    return;
  }

  checkName (name, ckParam, "invalid function parameter name ");

  if (pnwarnings) {
    const char* shadow = 0;
    if (currentTransition && currentTransition->getVariable (name))
      shadow = " shadows a variable";
    else if (getConstant (name) || getConstantFunction (name))
      shadow = " shadows a constant";
    else if (getType (name))
      shadow = " shadows a type name";

    if (shadow) {
      thePrinter.printRaw ("Warning:parameter ");
      thePrinter.printQuoted (name);
      thePrinter.printRaw (shadow);
      thePrinter.finish ();
    }
  }

  class VariableDefinition** v =
    new class VariableDefinition*[currentArity + 1];
  assert (!currentArity || currentParameters);
  assert (currentArity || !currentParameters);

  memcpy (v, currentParameters, currentArity * sizeof *v);
  delete[] currentParameters;
  currentParameters = v;
  currentParameters[currentArity++] =
    new class VariableDefinition (name, *type, false);
}

static const class Type*
getType (const char* name)
{
  for (const class Net* n = net; n; n = n->getParent ())
    if (const class Type* type = n->getType (name))
      return type;
  return 0;
}

static class Function*
getFunction (const char* name)
{
  class Function* f = currentTransition
    ? currentTransition->getFunction (name)
    : (currentContextTransition
       ? currentContextTransition->getFunction (name)
       : NULL);
  if (!f)
    for (class Net* n = net; n; n = const_cast<class Net*>(n->getParent ()))
      if ((f = n->getFunction (name)))
	break;
  return f;
}

static class Function*
getConstantFunction (const char* name)
{
  class Function* f = currentTransition
    ? currentTransition->getFunction (name)
    : (currentContextTransition
       ? currentContextTransition->getFunction (name)
       : NULL);
  if (!f || f->getArity ())
    for (class Net* n = net; n; n = const_cast<class Net*>(n->getParent ()))
      if ((f = n->getFunction (name)) && !f->getArity ())
	break;
  return f && f->getArity () ? NULL : f;
}

static const class Constant*
getConstant (const char* name)
{
  const class Constant* c = currentTransition
    ? currentTransition->getConstant (name)
    : (currentContextTransition
       ? currentContextTransition->getConstant (name)
       : NULL);
  if (!c)
    for (const class Net* n = net; n; n = n->getParent ())
      if ((c = n->getConstant (name)))
	break;
  return c;
}

static const class Expression*
getProposition (char* name)
{
  for (const class Net* n = net; n; n = n->getParent ())
    for (unsigned i = n->getNumProps (); i--; )
      if (!strcmp (n->getPropName (i), name))
	return &n->getProp (i);
  return 0;
}

static const class VariableDefinition*
getVariable (char* name)
{
  assert (name != NULL);

  const class VariableDefinition* v = getParameter (name);
  const class Function* f = getConstantFunction (name);
  const class Constant* c = getConstant (name);

  if (v || (v = currentVariables.find (name)))
    goto found;

  if (!currentTransition) {
    if (currentContextTransition &&
	(v = currentContextTransition->getVariable (name)));
    else if (!f && !c &&
	     !(currentType && currentType->getKind () == Type::tEnum &&
	       static_cast<const class EnumType*>(currentType)
	       ->getValue (name)) &&
	     !getProposition (name)) {
      thePrinter.printRaw ("undefined variable ");
      thePrinter.printQuoted (name);
      thePrinter.finish ();
      pnerrors++;
    }
    delete[] name;
  }
  else if ((v = currentTransition->getVariable (name))) {
  found:
    delete[] name;
  }
  else if (f || c)
    delete[] name;
  else if (!currentType) {
    thePrinter.printRaw ("cannot determine the type of variable ");
    thePrinter.printQuoted (name);
    thePrinter.finish ();
    pnerrors++;
    delete[] name;
  }
  else {
    assert (currentType != NULL);

    if (pnwarnings && getType (name)) {
      thePrinter.printRaw ("Warning:variable ");
      thePrinter.printQuoted (name);
      thePrinter.printRaw (" shadows a type name");
      thePrinter.finish ();
    }

    if (currentType->getKind () != Type::tEnum ||
	!static_cast<const class EnumType*>(currentType)->getValue (name)) {
      checkName (name, ckVar, "invalid (implicit) variable name ");
      v = &currentTransition->addVariable (name, *currentType, false, false);
    }
    else
      delete[] name;
  }

  return v;
}

static class Expression*
resolveName (char* name)
{
  const class Constant* c = getConstant (name);
  class Function* f = getConstantFunction (name);
  const card_t*const i =
    (currentType && currentType->getKind () == Type::tEnum)
    ? static_cast<const class EnumType*>(currentType)->getValue (name)
    : NULL;
  const class Expression* prop = getProposition (name);

  if (const class VariableDefinition* d = getVariable (name))
    return static_cast<class Variable*>((new class Variable (*d))->cse ());
  else if (c)
    return (const_cast<class Constant*>(c)->copy ())->cse ();
  else if (class Expression* expr = f ? f->expand (NULL) : NULL)
    return fold (expr);
  else if (i)
    return enumValue (*static_cast<const class EnumType*>(currentType), *i);
  else if (prop)
    return (const_cast<class Expression*>(prop)->copy ())->cse ();
  else
    return NULL;
}

static class Expression*
ensureSet (class Expression* expr,
	   const class Type* type)
{
  return !expr || expr->isSet () ? expr : ensureMarking (expr, type);
}

static class Marking*
ensureMarking (class Expression* expr,
	       const class Type* type,
	       bool temporal)
{
  if (!expr)
    return NULL;
  else if (expr->getKind () == Expression::eUndefined) {
    pnerror ("undefined marking expressions are not allowed");
    expr->destroy ();
    return NULL;
  }

  assert (expr->getType () || expr->getKind () == Expression::eEmptySet);

  if (expr->getKind () == Expression::eMarking)
    return static_cast<class Marking*>(expr);
  else if (expr->isSet () || expr->isBasic ()) {
    if (currentPlace) {
      if (!type)
	type = &currentPlace->getType ();
      if (!makeCompatible (expr, *type)) {
	pnerror ("marking expression has incompatible type");
	return NULL;
      }
    }

    if (!type || makeCompatible (expr, *type)) {
      class Marking* m = new class Marking (type ? 0 : currentPlace);
      m->setToken (expr);
      return m;
    }
    else
      return NULL;
  }
  else if (temporal) {
    class Marking* m = new class Marking (currentPlace);
    m->setToken (expr);
    return m;
  }
  else {
    pnerror ("marking expression expected");
    expr->destroy ();
    return NULL;
  }
}

static class Expression*
noMarking (class Expression* expr)
{
  if (expr && expr->isSet ()) {
    pnerror ("marking expression not allowed here");
    expr->destroy ();
    return NULL;
  }

  return expr;
}

static class Expression*
ensureExpr (class Expression* expr)
{
  expr = noMarking (expr);

  if (expr && !expr->isBasic ()) {
    pnerror ("basic expression expected");
    expr->destroy (); return NULL;
  }

  return expr;
}

static class Expression*
ensureBool (class Expression* expr, bool undefined)
{
  expr = noMarking (expr);

  if (!expr)
    return NULL;

  if (expr->getKind () == Expression::eUndefined) {
    if (undefined)
      return expr;
    pnerror ("undefined or fatal expression is not meaningful here");
    expr->destroy (); return NULL;
  }

  if (!expr->getType ()) {
    pnerror ("expression has no type");
    expr->destroy (); return NULL;
  }

  if (expr->getType ()->getKind () != Type::tBool) {
    pnerror ("boolean expression expected");
    expr->destroy (); return NULL;
  }

  return expr;
}

static class Expression*
ensureInt (class Expression* expr)
{
  expr = ensureExpr (expr);

  if (!expr)
    return NULL;

  if (!expr->getType ()) {
    pnerror ("expression has no type");
    expr->destroy (); return NULL;
  }

  switch (expr->getType ()->getKind ()) {
  case Type::tInt:
    return expr;
  case Type::tEnum:
  case Type::tChar:
  case Type::tCard:
  case Type::tBool:
    if (!makeCompatible (expr, Net::getIntType (), true));
    else if (expr->getType () == &Net::getIntType ())
      return expr;
    /* fall through */
  default:
    pnerror ("signed integer expression expected");
    expr->destroy ();
    return NULL;
  }
}

static class Expression*
ensureCard (class Expression* expr)
{
  expr = ensureExpr (expr);

  if (!expr)
    return NULL;

  if (!expr->getType ()) {
    pnerror ("expression has no type");
    expr->destroy (); return NULL;
  }

  switch (expr->getType ()->getKind ()) {
  case Type::tCard:
    return expr;
  case Type::tEnum:
  case Type::tChar:
  case Type::tInt:
  case Type::tBool:
    if (!makeCompatible (expr, Net::getCardType (), true));
    else if (expr->getType () == &Net::getCardType ())
      return expr;
    /* fall through */
  default:
    pnerror ("unsigned integer expression expected");
    expr->destroy ();
    return NULL;
  }
}

static class Expression*
ensureIntCard (class Expression* expr)
{
  expr = ensureExpr (expr);

  if (!expr)
    return NULL;

  if (!expr->getType ()) {
    pnerror ("expression has no type");
    expr->destroy (); return NULL;
  }

  switch (expr->getType ()->getKind ()) {
  case Type::tCard:
  case Type::tInt:
    return expr;
  case Type::tEnum:
  case Type::tChar:
  case Type::tBool:
    if (!makeCompatible (expr, Net::getCardType (), true));
    else if (expr->getType () == &Net::getCardType () ||
	     expr->getType () == &Net::getIntType ())
      return expr;
    /* fall through */
  default:
    pnerror ("integer expression expected");
    expr->destroy ();
    return NULL;
  }
}

static class Expression*
ensureBuffer (class Expression* expr)
{
  expr = ensureExpr (expr);

  if (!expr)
    return NULL;

  if (!expr->getType ()) {
    pnerror ("expression has no type");
    expr->destroy (); return NULL;
  }

  if (expr->getType ()->getKind () != Type::tBuffer) {
    pnerror ("buffer expression expected");
    expr->destroy (); return NULL;
  }

  return expr;
}

static class Expression*
ensureOrdered (class Expression* expr)
{
  expr = ensureExpr (expr);

  if (!expr)
    return NULL;

  if (!expr->getType ()) {
    pnerror ("expression has no type");
    expr->destroy (); return NULL;
  }

  if (!isOrdered (*expr->getType ())) {
    expr->destroy (); return NULL;
  }

  return expr;
}

static void
addEnumeration (class EnumType* e, char* name)
{
  assert (e && name);
  const card_t* const i = e->getValue (name);
  if (i) {
    thePrinter.printRaw ("enumeration constant ");
    thePrinter.printQuoted (name);
    thePrinter.printRaw (" already defined as ");
    thePrinter.print (*i);
    thePrinter.finish ();
    pnerrors++;
    delete[] name;
  }
  else
    e->addEnumeration (name);
  if (pnwarnings) {
    if (getConstant (name)) {
      thePrinter.printRaw ("Warning:enumeration constant ");
      thePrinter.printQuoted (name);
      thePrinter.printRaw (" shadows a constant");
      thePrinter.finish ();
    }
    else if (getType (name)) {
      thePrinter.printRaw ("Warning:enumeration constant ");
      thePrinter.printQuoted (name);
      thePrinter.printRaw (" shadows a type name");
      thePrinter.finish ();
    }
  }
}

static void
addEnumeration (class EnumType* e, char* name, card_t value)
{
  assert (e && name);
  const card_t* const i = e->getValue (name);
  if (e->getValue (name)) {
    thePrinter.printRaw ("enumeration constant ");
    thePrinter.printQuoted (name);
    thePrinter.printRaw (" already defined as ");
    thePrinter.print (*i);
    thePrinter.finish ();
    pnerrors++;
    delete[] name;
  }
  else if (e->hasValue (value)) {
    thePrinter.printRaw ("enumeration already contains the value ");
    thePrinter.print (value);
    thePrinter.finish ();
    pnerrors++;
    delete[] name;
  }
  else
    e->addEnumeration (name, value);
  if (pnwarnings) {
    if (getConstant (name)) {
      thePrinter.printRaw ("Warning:enumeration constant ");
      thePrinter.printQuoted (name);
      thePrinter.printRaw (" shadows a constant");
      thePrinter.finish ();
    }
    else if (getType (name)) {
      thePrinter.printRaw ("Warning:enumeration constant ");
      thePrinter.printQuoted (name);
      thePrinter.printRaw (" shadows a type name");
      thePrinter.finish ();
    }
  }
}

static class Value*
ensureConstant (class Expression* expr, const class Type* type)
{
  if (!(expr = fold (expr)))
    return NULL;

  if (!type) {
    expr->destroy ();
    return NULL;
  }

  if (!isOrdered (*type)) {
    expr->destroy ();
    return NULL;
  }

  class Value* v = NULL;
  if (expr->getKind () == Expression::eConstant) {
    const class Valuation empty;
    v = expr->eval (empty);
    assert (!!v);
  }
  else
    pnerror ("constant expected");

  expr->destroy ();

  switch (type->getKind ()) {
  case Type::tInt:
  case Type::tCard:
  case Type::tBool:
  case Type::tChar:
  case Type::tEnum:
    break;
  default:
    if (&v->getType () == type)
      return v;
    thePrinter.printRaw ("unable to convert constant from ");
    printType (v->getType ());
    thePrinter.printRaw (" to ");
    printType (*type);
    thePrinter.finish ();
    pnerrors++;
    delete v;
    return NULL;
  }

  /** the integer value */
  struct {
    /** tag: is the value unsigned */
    bool u;
    /** the actual value */
    union {
      int_t i; ///< signed
      card_t u; ///< unsigned
    } v;
  } value;

  switch (v->getType ().getKind ()) {
  case Type::tInt:
    assert (v->getKind () == Value::vLeaf);
    if (&v->getType () == type)
      return v;
    value.u = false, value.v.i = int_t (static_cast<class LeafValue&>(*v));
    break;
  case Type::tCard:
    assert (v->getKind () == Value::vLeaf);
    if (&v->getType () == type)
      return v;
    value.u = true, value.v.u = card_t (static_cast<class LeafValue&>(*v));
    break;
  case Type::tBool:
    assert (v->getKind () == Value::vLeaf);
    if (&v->getType () == type)
      return v;
    value.u = true, value.v.u = bool (static_cast<class LeafValue&>(*v));
    break;
  case Type::tChar:
    assert (v->getKind () == Value::vLeaf);
    if (&v->getType () == type)
      return v;
    value.u = false, value.v.i = int_t (static_cast<class LeafValue&>(*v));
    break;
  case Type::tEnum:
    assert (v->getKind () == Value::vLeaf);
    if (&v->getType () == type)
      return v;
    value.u = true, value.v.u = card_t (static_cast<class LeafValue&>(*v));
    break;

  default:
    if (&v->getType () == type)
      return v;
    thePrinter.printRaw ("cannot define constraint: incompatible type ");
    printType (v->getType ());
    thePrinter.finish ();
    pnerrors++;
    delete v;
    return NULL;
  }

  if (pnwarnings && v->getType ().getKind () != type->getKind () &&
      !((type->getKind () == Type::tInt || type->getKind () == Type::tCard) &&
	(v->getType ().getKind () == Type::tInt ||
	 v->getType ().getKind () == Type::tCard))) {
    thePrinter.printRaw ("Warning:converting constant ");
    thePrinter.print (value.u ? value.v.u : value.v.i);
    thePrinter.printRaw (" from ");
    printType (v->getType ());
    thePrinter.printRaw (" to ");
    printType (*type);
    thePrinter.finish ();
  }

  delete v;
  v = NULL;
  switch (type->getKind ()) {
  case Type::tInt:
    if (value.u) {
      if (value.v.u > INT_T_MAX)
	break;
      value.v.i = value.v.u, value.u = false;
    }
    if (type->isConstrained (*(v = new class LeafValue (*type, value.v.i))))
      return v;
    delete v;
    break;
  case Type::tCard:
    if (!value.u) {
      if (value.v.i < 0)
	break;
      value.v.u = value.v.i, value.u = true;
    }
    if (type->isConstrained (*(v = new class LeafValue (*type, value.v.u))))
      return v;
    delete v;
    break;
  case Type::tBool:
    if (value.u) {
      if ((value.v.u == 0 || value.v.u == 1) &&
	  type->isConstrained (*(v = new class LeafValue (*type,
							  value.v.u == 1))))
	return v;
    }
    else {
      if ((value.v.i == 0 || value.v.i == 1) &&
	  type->isConstrained (*(v = new class LeafValue (*type,
							  value.v.i == 1))))
	return v;
    }
    delete v;
    break;
  case Type::tChar:
    if (!value.u) {
      if (value.v.i < 0)
	break;
      value.v.u = value.v.i, value.u = true;
    }
    if (value.v.u <= CHAR_T_MAX &&
	type->isConstrained (*(v = new class LeafValue (*type, value.v.u))))
      return v;
    delete v;
    break;
  case Type::tEnum:
    if (!value.u) {
      if (value.v.i < 0)
	break;
      value.v.u = value.v.i, value.u = true;
    }
    if (type->isConstrained (*(v = new class LeafValue (*type, value.v.u))) ||
	!type->getConstraint ())
      return v;
    delete v;
    break;

  default:
    assert (false);
  }

  thePrinter.printRaw ("value ");
  thePrinter.print (value.u ? value.v.u : value.v.i);
  thePrinter.printRaw (" out of range for type ");
  printType (*type);
  thePrinter.finish ();
  pnerrors++;
  return NULL;
}

static class Expression*
enumValue (const class EnumType& type, card_t value)
{
  class LeafValue* e = new class LeafValue (type, value);
  if (type.isConstrained (*e))
    return (new class Constant (*e))->cse ();
  else {
    thePrinter.printRaw ("enumeration constant ");
    if (const char* c = type.getEnumName (value)) {
      thePrinter.print (c);
      thePrinter.delimiter ('=');
    }
    thePrinter.print (value);
    thePrinter.printRaw (" out of range for type ");
    printType (type);
    thePrinter.finish ();
    pnerrors++;
    delete e;
    return NULL;
  }
}

static class Expression*
namedComponent (char* name, class Expression* expr)
{
  if (!name) {
    expr->destroy ();
    return NULL;
  }

  if (!currentContextType || currentContextType->getKind () != Type::tStruct) {
    thePrinter.printRaw ("named component ");
    thePrinter.printQuoted (name);
    thePrinter.printRaw (" can only be used with a struct");
    thePrinter.finish ();
    pnerrors++;
    expr->destroy ();
    delete[] name;
    return NULL;
  }

  const class StructType& type =
    static_cast<const class StructType&>(*currentContextType);

  if (currentComponent < type.getSize ()) {
    const char* comp = type.getComponentName (currentComponent);
    if (strcmp (name, comp)) {
      thePrinter.printRaw ("expecting named component ");
      thePrinter.printQuoted (comp);
      thePrinter.printRaw (" instead of ");
      thePrinter.printQuoted (name);
      thePrinter.finish ();
      pnerrors++;
      expr->destroy ();
      delete[] name;
      return NULL;
    }
  }
  else {
    thePrinter.printRaw ("extraneous named component ");
    thePrinter.printQuoted (name);
    thePrinter.printRaw (" at end of struct");
    thePrinter.finish ();
    pnerrors++;
    expr->destroy ();
    delete[] name;
    return NULL;
  }

  delete[] name;
  return expr;
}

static void
beginAny (class Type*& type, char* name,
	  bool hidden)
{
  if (type) {
    if (currentTransition) {
      if (currentTransition->getKind ()) {
	thePrinter.printRaw ("assertion transitions do not output");
	thePrinter.finish ();
	pnerrors++;
	type = NULL; delete[] name;
      }
      else if (name && currentTransition->getVariable (name)) {
	thePrinter.printRaw ("output variable ");
	thePrinter.printQuoted (name);
	thePrinter.printRaw (" shadows another variable");
	thePrinter.finish ();
	pnerrors++;
	type = NULL; delete[] name;
      }
      else {
	if (name) {
	  checkIterator (name);
	  currentTransition->addVariable (name, *type, true, hidden);
	}
	card_t size = type->getNumValues ();
	if (!size || size == CARD_T_MAX || (maxRange && size > maxRange)) {
	  thePrinter.printRaw ("type ");
	  printType (*type);
	  thePrinter.printRaw (" has too many (");
	  thePrinter.print (size);
	  thePrinter.printRaw (") values for non-determinism");
	  if (size && size < CARD_T_MAX)
	    thePrinter.printRaw (" (try -q0)");
	  thePrinter.finish ();
	  pnerrors++;
	}
      }
    }
    else {
      pnerror ("non-determinism is only allowed on output arcs");
      type = NULL; delete[] name;
    }
  }
  else
    delete[] name;

  pushTypeContext (type);
}

static class Variable*
endAny (const class Type* type,
	char* name,
	class Expression* condition)
{
  class Variable* var;
  popTypeContext ();

  if (type) {
    if (!condition || (condition = ensureBool (ensureExpr (condition)))) {
      var = static_cast<class Variable*>
	((new class Variable
	  (currentTransition->addOutputVariable (*type, name,
						 condition)))->cse ());
      assert (var != NULL);
      if (pnwarnings && condition) {
	class VariableSet vars;
	vars.insert (var->getVariable ());
	if (!condition->depends (vars, false)) {
	  thePrinter.printRaw ("Warning:condition is independent of ");
	  thePrinter.printQuoted (var->getVariable ().getName ());
	  thePrinter.finish ();
        }
      }
    }
    else
      var = NULL, condition->destroy ();
  }
  else
    var = NULL, condition->destroy ();
  return var;
}

static void
beginQuantifier (char* name, const class Type* type)
{
  if (name && type) {
    checkIterator (name);
    if (pnwarnings) {
      const char* shadow = 0;
      if (currentVariables.find (name))
        shadow = " shadows another quantifier";
      else if ((currentTransition &&
		currentTransition->getVariable (name)) ||
	       (currentContextTransition &&
		currentContextTransition->getVariable (name)))
	shadow = " shadows a transition variable";
      else if (getConstant (name))
	shadow = " shadows a constant";
      else if (currentType && currentType->getKind () == Type::tEnum &&
	       static_cast<const class EnumType*>(currentType)->
	       getValue (name))
	shadow = " shadows an enumeration constant";
      else if (getType (name))
	shadow = " shadows a type name";

      if (shadow) {
	thePrinter.printRaw ("quantifier ");
	thePrinter.printQuoted (name);
	thePrinter.printRaw (shadow);
	thePrinter.finish ();
      }
    }

    card_t size = type->getNumValues ();
    if (!size || size == CARD_T_MAX || (maxRange && size > maxRange)) {
      thePrinter.printRaw ("type ");
      printType (*type);
      thePrinter.printRaw (" has too many (");
      thePrinter.print (size);
      thePrinter.printRaw (") values for quantification");
      if (size && size < CARD_T_MAX)
	thePrinter.printRaw (" (try -q0)");
      thePrinter.finish ();
      pnerrors++;
    }
    currentVariables.push (new class VariableDefinition (name, *type, true));
  }

  struct TypeContext tc = {
    currentContextType,
    currentType,
    currentComponent
  };
  typeStack.push (tc);

  currentContextType = NULL;
  currentComponent = 0;
  currentType = type;
}

static void
continueQuantifier (ASSERT1 (const class Type* type))
{
  assert (!currentContextType);
  assert (currentType == type);
  assert (!typeStack.empty ());
  struct TypeContext tc = typeStack.top ();
  typeStack.pop ();

  currentContextType = tc.context;
  currentType = tc.component;
  currentComponent = tc.numComponent;
}

static class Expression*
endQuantifier (char* name,
	       const class Type* type,
	       class Expression* condition,
	       unsigned kind,
	       class Expression* expr,
	       bool temporal)
{
  if (name && type && expr && kind) {
    class VariableDefinition* var = currentVariables.top ();
    currentVariables.pop ();

    card_t size = var->getType ().getNumValues ();
    if (!size || size == CARD_T_MAX || (maxRange && size > maxRange)) {
      condition->destroy ();
      expr->destroy ();
      delete var;
      return NULL;
    }

    if ((condition = ensureBool (ensureExpr (condition))) && pnwarnings) {
      class VariableSet vars;
      vars.insert (*var);
      if (!condition->depends (vars, false)) {
	thePrinter.printRaw ("Warning:condition is independent of ");
	thePrinter.printQuoted (var->getName ());
	thePrinter.finish ();
      }
    }

    class Valuation v;
    switch (kind) {
    case 1:
      if (class Marking* m = ensureMarking (expr, currentType, temporal)) {
	expr = m->quantify (v,
			    currentTransition
			    ? currentTransition
			    : currentContextTransition,
			    *var, condition,
			    currentTransition && !temporal);
	m->destroy ();
	condition->destroy ();
	delete var;
	if (!expr) {
	  if (check (v))
	    pnerror ("conflicting variables in quantification");
	}
	else
	  assert (v.isOK ());
	return expr->cse ();
      }
      else
	expr = NULL;
      break;
    case 2:
    case 3:
      if ((expr = ensureBool (expr))) {
	class Expression* e =
	  BooleanBinop::quantify (kind != 2,
				  *expr, v,
				  currentTransition
				  ? currentTransition
				  : currentContextTransition,
				  *var, condition,
				  currentTransition && !temporal);
	expr->destroy ();
	delete var;
	if (!e) {
	  if (check (v))
	    pnerror ("conflicting variables in quantification");
	}
	else
	  assert (v.isOK ());
	return e->cse ();
      }
      break;
    default:
      assert (false);
    }

    expr->destroy ();
    delete var;
    return NULL;
  }
  else {
    condition->destroy (), expr->destroy ();
    if (name && type) {
      class VariableDefinition* var = currentVariables.top ();
      currentVariables.pop ();
      delete var;
    }
    else
      delete[] name;
    return NULL;
  }
}

static class Expression*
quantifierVariable (char* prefix, char* quant, bool p)
{
  char delimiter = p ? ',' : '.';
  if (!prefix) {
    delete[] prefix;
    delete[] quant;
    return NULL;
  }
  class VariableDefinition* var;
  if (currentVariables.empty ())
    var = NULL;
  else if (quant)
    var = currentVariables.find (quant);
  else
    var = currentVariables.top ();

  if (!var) {
    thePrinter.delimiter (delimiter);
    thePrinter.print (prefix);
    if (quant) {
      thePrinter.printRaw (": quantifier ");
      thePrinter.print (quant);
      thePrinter.printRaw (" not found");
    }
    else
      thePrinter.printRaw (": no quantifier found");
    thePrinter.finish ();
    pnerrors++;
  }
  else if (!var->isAggregate ()) {
    thePrinter.delimiter (delimiter);
    thePrinter.print (prefix);
    thePrinter.printRaw (": variable ");
    thePrinter.print (var->getName ());
    thePrinter.printRaw (" is not a quantifier");
    thePrinter.finish ();
    pnerrors++;
  }
  else if (!currentTransition && !currentContextTransition) {
    thePrinter.printRaw ("quantifier ");
    thePrinter.print (var->getName ());
    thePrinter.printRaw (": cannot define ");
    thePrinter.delimiter (delimiter);
    thePrinter.print (prefix);
    thePrinter.printRaw (" outside transition");
    thePrinter.finish ();
    pnerrors++;
  }
  else if (!currentType) {
    if (const class VariableDefinition* qvar = var->findChild (prefix)) {
      delete[] quant;
      delete[] prefix;
      return (new class Variable (*qvar, p))->cse ();
    }
    else {
      thePrinter.printRaw ("quantifier ");
      thePrinter.print (var->getName ());
      thePrinter.printRaw (": cannot determine the type of ");
      thePrinter.delimiter (delimiter);
      thePrinter.print (prefix);
      thePrinter.finish ();
      pnerrors++;
    }
  }
  else if (const class VariableDefinition* qvar =
	   var->addChild (prefix, *currentType)) {
    delete[] quant;
    return (new class Variable (*qvar, p))->cse ();
  }
  else {
    thePrinter.printRaw ("quantifier ");
    thePrinter.print (var->getName ());
    thePrinter.printRaw (": contradictory type for ");
    thePrinter.delimiter (delimiter);
    thePrinter.print (prefix);
    thePrinter.finish ();
    pnerrors++;
  }

  delete[] prefix;
  delete[] quant;
  return NULL;
}

static bool
makeCompatible (class Expression*& expr,
		const class Type& type,
		bool casting)
{
  if (!expr) return false;
  if (expr->getType () == &type ||
      expr->getKind () == Expression::eEmptySet)
    return true;
  assert (!!expr->getType ());
  const class Type& srcType = *expr->getType ();
  if (((Typecast::isCastable (type) &&
	Typecast::isCastable (srcType)) ||
       Type::isCompatible (srcType, type)) &&
      expr->isBasic () && !expr->isSet ()) {
    bool isConstant = expr->getKind () == Expression::eConstant;
    expr = (new class Typecast (type, *expr))->cse ();
    if (!casting && pnwarnings &&
	((!srcType.isAssignable (type) &&
	  srcType.getKind () != Type::tCard &&
	  srcType.getKind () != Type::tInt &&
	  type.getKind () != Type::tCard &&
	  type.getKind () != Type::tInt) ||
	 (!isConstant && !srcType.isAlwaysAssignable (type)))) {
      thePrinter.printRaw ("Warning:implicit conversion from ");
      printType (srcType);
      thePrinter.printRaw (" to ");
      printType (type);
      thePrinter.finish ();
    }
    if (isConstant && !(expr = fold (expr))) {
      thePrinter.printRaw ("cannot convert constant from ");
      printType (srcType);
      thePrinter.printRaw (" to ");
      printType (type);
      thePrinter.finish ();
      pnerrors++;
      return false;
    }
    return true;
  }
  else {
    thePrinter.printRaw ("cannot convert from ");
    printType (srcType);
    thePrinter.printRaw (" to ");
    printType (type);
    thePrinter.finish ();
    pnerrors++;
    return false;
  }
}

static bool
isCompatible (class Expression*& expr,
	      const class Type* type)
{
  if (!type) {
    pnerror ("too many components for struct");
    return false;
  }

  if (!expr)
    return false;

  if (!expr->getType ()) {
    pnerror ("expression has no type");
    return false;
  }

  return makeCompatible (expr, *type);
}

static bool
isCompatible (class Expression*& expr,
	      const class StructExpression& s)
{
  const class Type* type = s.getNextType ();
  const char* structName = s.getType ()->getName ();

  if (!type) {
    if (structName) {
      thePrinter.printRaw ("too many components for struct ");
      thePrinter.printQuoted (structName);
    }
    else
      thePrinter.printRaw ("too many components for struct");
    thePrinter.finish ();
    pnerrors++;
    return false;
  }

  const char* comp =
    static_cast<const class StructType*>(s.getType ())
    ->getComponentName (s.size ());

  if (!expr) {
    if (structName) {
      thePrinter.printRaw ("error in expression for struct ");
      thePrinter.printQuoted (structName);
      thePrinter.printRaw (" component ");
    }
    else
      thePrinter.printRaw ("error in expression for struct component ");
    thePrinter.printQuoted (comp);
    thePrinter.finish ();
    pnerrors++;
    return false;
  }

  if (!expr->getType ()) {
    if (structName) {
      thePrinter.printRaw ("expression for struct ");
      thePrinter.printQuoted (structName);
      thePrinter.printRaw (" component ");
    }
    else
      thePrinter.printRaw ("expression for struct component ");
    thePrinter.printQuoted (comp);
    thePrinter.printRaw (" has no type");
    thePrinter.finish ();
    pnerrors++;
    return false;
  }

  if (!makeCompatible (expr, *type))
    return false;

  if (!expr->getType ()->isAlwaysAssignable (*type)) {
    if (Type::isCompatible (*expr->getType (), *type))
      thePrinter.printRaw ("too broad type for struct ");
    else
      thePrinter.printRaw ("type mismatch for struct ");
    if (structName) {
      thePrinter.printQuoted (structName);
      thePrinter.printRaw (" component ");
    }
    else
      thePrinter.printRaw ("component ");
    thePrinter.printQuoted (comp);
    thePrinter.finish ();
    pnerrors++;
    return false;
  }
  else if (expr->getType () != type)
    expr = (new class Typecast (*type, *expr))->cse ();

  return true;
}

static bool
isOrdered (const class Type& type)
{
  if (!type.isOrdered ()) {
    thePrinter.printRaw ("unordered type ");
    printType (type);
    thePrinter.finish ();
    pnerrors++;
    return false;
  }

  return true;
}

static bool
check (const class Valuation& v)
{
  bool ok = true;

  switch (v.getError ()) {
  case errDiv0:
    ok = false, pnerror ("division by zero"); break;
  case errOver:
    ok = false, pnerror ("overflow"); break;
  case errMod:
    ok = false, pnerror ("modulus error"); break;
  case errShift:
    ok = false, pnerror ("shift error"); break;
  case errConst:
    ok = false, pnerror ("constraint violation"); break;
  case errUnion:
    ok = false, pnerror ("union violation"); break;
  case errBuf:
    ok = false, pnerror ("buffer violation"); break;
  case errCard:
    ok = false, pnerror ("cardinality overflow"); break;
  case errComp:
    assert (false);
  case errNone:
  case errVar:
  case errUndef:
  case errFatal:
    break;
  }

  return ok;
}

static class Expression*
fold (class Expression* expr)
{
  if (expr && expr->isBasic () &&
      expr->getKind () != Expression::eConstant) {
    class Valuation v;
    if (class Value* value = expr->eval (v)) {
      assert (v.isOK ());
      expr->destroy ();
      return (new class Constant (*value))->cse ();
    }
    else {
      if (!check (v)) {
	expr->destroy ();
	return NULL;
      }
      else {
	if (v.getError () == errFatal) {
	  expr->destroy ();
	  return (new class Undefined (Undefined::fFatalError))->cse ();
	}
	if (v.getError () == errUndef) {
	  expr->destroy ();
	  return (new class Undefined (Undefined::fError))->cse ();
	}

	assert (v.getError () == errVar);
	return expr->cse ();
      }
    }
  }
  else
    return expr->cse ();
}

static void
defun (const class Type* type,
       char* name,
       class Expression* expr)
{
  currentType = NULL;
  if (!currentArity && type && !type->isLeaf () && name && expr &&
      expr->getKind () == Expression::eConstant) {
    if (isCompatible (expr, type)) {
      if (currentTransition) {
	if (!currentTransition->addConstant
	    (name, *static_cast<class Constant*>(expr))) {
	redef:
	  thePrinter.printRaw ("ignoring redefinition of constant ");
	  thePrinter.printQuoted (name);
	  thePrinter.finish ();
	  pnerrors++;
	  delete[] name, expr->destroy ();
	}
	else if (pnwarnings && theNet.getConstant (name)) {
	  thePrinter.printRaw ("Warning:definition of constant ");
	  thePrinter.printQuoted (name);
	  thePrinter.printRaw (" shadows the global definition");
	  thePrinter.finish ();
	}
      }
      else if (!theNet.addConstant
	       (name, *static_cast<class Constant*>(expr)))
	goto redef;
    }
  }
  else if (type && name && expr) {
    if ((currentTransition && currentTransition->getFunction (name)) ||
	(!currentTransition && theNet.getFunction (name))) {
      thePrinter.printRaw ("ignoring redefinition of function ");
      thePrinter.printQuoted (name);
      thePrinter.finish ();
      pnerrors++;
      delete[] name, expr->destroy ();
    }
    else if (isCompatible (expr, type)) {
      class Function* f =
	new class Function (name, currentArity, currentParameters, expr);
      if (currentTransition) {
	currentTransition->addFunction (*f);
	if (pnwarnings && theNet.getFunction (name)) {
	  thePrinter.printRaw ("Warning:definition of function ");
	  thePrinter.printQuoted (name);
	  thePrinter.printRaw (" shadows the global definition");
	  thePrinter.finish ();
	}
      }
      else
	theNet.addFunction (*f);

      if (pnwarnings && getType (name)) {
	thePrinter.printRaw ("Warning:function ");
	thePrinter.printQuoted (name);
	thePrinter.printRaw (" shadows a type name");
	thePrinter.finish ();
	for (unsigned i = 0; i < currentArity; i++) {
	  class VariableSet vars;
	  vars.insert (*currentParameters[i]);
	  if (!expr->depends (vars, false)) {
	    thePrinter.printRaw ("Warning:function ");
	    thePrinter.printQuoted (name);
	    thePrinter.printRaw (" does not use parameter ");
	    thePrinter.printQuoted (currentParameters[i]->getName ());
	    thePrinter.finish ();
	  }
	}
      }
    }
    else
      delete[] name, expr->destroy ();
  }
  else
    delete[] name, expr->destroy ();
  currentArity = 0, currentParameters = 0;
  emptyParsed = false;
}

static class Expression*
newRelopExpression (bool equal,
		    class Expression* left,
		    class Expression* right)
{
  left = ensureExpr (left); right = ensureExpr (right);
  if (!left || !right);
  else if (left->getType () != right->getType ()) {
    if (left->getKind () == Expression::eConstant &&
	makeCompatible (left, *right->getType ()))
      goto ok;
    if (right->getKind () == Expression::eConstant &&
	makeCompatible (right, *left->getType ()))
      goto ok;
    pnerror ("expressions in comparison have different types");
  }
  else if (!left->getType ()->isOrdered () && !equal)
    pnerror ("cannot compare expressions of unordered type");
  else {
  ok:
    return fold (RelopExpression::construct (equal, *left, *right));
  }

  left->destroy ();
  right->destroy ();
  return 0;
}

static class Expression*
newBinopExpression (enum BinopExpression::Op op,
		    class Expression* left,
		    class Expression* right)
{
  if (left && left->getType () &&
      left->getType ()->getKind () == Type::tVector) {
    bool shl = false;
    switch (op) {
    case BinopExpression::bRol:
      shl = true;
      break;
    case BinopExpression::bRor:
      shl = false;
      break;
    default:
      ensureIntCard (left)->destroy ();
      ensureIntCard (right)->destroy ();
      return NULL;
    }
    if ((left = ensureExpr (left)) && (right = ensureCard (right))) {
      size_t size =
	static_cast<const class VectorType*>(left->getType ())->getSize ();

      if (shl ||
	  (right = fold (new class BinopExpression
			  (BinopExpression::bMinus,
			   *new class Constant (*new class LeafValue
						(Net::getCardType (),
						 card_t (size))), *right)))) {
	if (right->getKind () == Expression::eConstant) {
	  const class Value& v =
	    static_cast<class Constant*>(right)->getValue ();
	  assert (v.getKind () == Value::vLeaf);
	  card_t amount = static_cast<const class LeafValue&>(v);
	  if (!(amount %= size)) {
	    right->destroy ();
	    return left;
	  }
	}
	return fold (new class VectorShift (*left, *right));
      }
    }
    left->destroy (), right->destroy ();
    return NULL;
  }

  left = ensureIntCard (left);
  right = ensureIntCard (right);
  if (!left || !right) {
    left->destroy (), right->destroy ();
    return NULL;
  }

  if (!(right = left->getType ()->getKind () == Type::tCard
	? ensureCard (right) : ensureInt (right))) {
    left->destroy ();
    return NULL;
  }

  return fold (new class BinopExpression (op, *left, *right));
}

static class Expression*
newNotExpression (class Expression* expr)
{
  if ((expr = ensureBool (expr)))
    expr = fold (NotExpression::construct (*expr));
  return expr;
}

static class Expression*
newBooleanBinop (bool conj,
		 class Expression* left,
		 class Expression* right)
{
  assert (left && right);
  assert (left->getKind () == Expression::eUndefined ||
	  (left->getType () && left->getType ()->getKind () == Type::tBool));
  assert (right->getKind () == Expression::eUndefined ||
	  (right->getType () && right->getType ()->getKind () == Type::tBool));
  assert (left == left->cse ());
  assert (right == right->cse ());

  return (BooleanBinop::construct (conj, *left, *right))->cse ();
}

static class Expression*
newSetExpression (enum SetExpression::Op op,
		  class Expression* left,
		  class Expression* right)
{
  const class Type* type = 0;
  if (right && right->isSet ())
    type = right->getType ();
  else if (left && left->isSet ())
    type = left->getType ();
  left = ensureSet (left, type);
  right = ensureSet (right, type);
  if (!left || !right);
  else if (left->getType () == right->getType () ||
	   left->getKind () == Expression::eEmptySet ||
	   right->getKind () == Expression::eEmptySet)
    return SetExpression::construct (op, *left, *right);
  else
    pnerror ("expressions have incompatible types");
  left->destroy (), right->destroy ();
  return NULL;
}

static class Expression*
newCardinalityExpression (enum CardinalityExpression::Op op,
			  class Expression* expr)
{
  if (class Expression* m = ensureSet (expr)) {
    if (m->getKind () == Expression::eEmptySet) {
      m->destroy ();
      pnwarn ("the cardinality of an empty set is always zero");
      return (new class Constant (*new class LeafValue
				  (Net::getCardType (),
				   op == CardinalityExpression::cMin
				   ? CARD_T_MAX
				   : 0)))->cse ();
    }
    else
      return (new class CardinalityExpression (op, *m))->cse ();
  }
  else
    return NULL;
}

static class Expression*
newBufferUnop (enum BufferUnop::Op op, class Expression* expr)
{
  if (!(expr = ensureBuffer (expr)))
    return NULL;

  if (expr->getKind () == Expression::eBufferIndex) {
    class BufferIndex* b = static_cast<class BufferIndex*>(expr);
    class Expression* buffer = &b->getBuffer ();
    class Expression* i = &b->getIndex ();
    b->destroy ();
    switch (op) {
    case BufferUnop::bFree:
    case BufferUnop::bUsed:
      if (i) {
	pnwarn ("ignoring meaningless buffer indexing expression");
	i->destroy (), i = NULL;
      }
      break;
    case BufferUnop::bPeek:
      break;
    }

    return fold (new class BufferUnop (op, *buffer, i));
  }

  return fold (new class BufferUnop (op, *expr, NULL));
}
