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

%{
#ifndef BSD
# ifdef __APPLE__
#  define BSD
# elif defined __FreeBSD__||defined __OpenBSD__||defined __NetBSD__
#  define BSD
# elif defined _AIX
#  define BSD
# endif
#endif
#ifdef __WIN32
# undef __STRICT_ANSI__
# include <io.h>
# undef chdir
# define chdir(path) _chdir (path)
#elif !defined __CYGWIN__ && !defined BSD
# include <alloca.h>
#endif
#include <unistd.h> // for chdir(2)
#include <stdlib.h> // for getenv(3)

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

#include "allExpressions.h"
#include "ExpressionSet.h"

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

#include "Quantifier.h"

#include "typedefs.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 marde.y
   * Parser for the query language
   */

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

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

  MARIA is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  MARIA is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

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

  /** flag: exiting */
  bool exiting = false;
  /** character to display on its own line before the prompt (0=none) */
  static char promptchar = 0;
  /** warning flag (true=print warnings) */
  extern bool dewarnings;
  /** Maximum range size of quantifications */
  extern card_t maxRange;
#ifndef BUILTIN_LTL
  /** Translator for property automata */
  extern char* translator;
#endif // !BUILTIN_LTL
#ifdef EXPR_COMPILE
  /** name of the compilation directory (NULL=don't compile) */
  extern char* compiledir;
#endif // EXPR_COMPILE

  /** read a command line */
  const char* deline (void);
  /** reset the parser and the lexical analyzer */
  void dereset ();
  /** reset the lexical analyzer */
  extern void delexreset ();

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

  /** flag: expecting a command keyword */
  bool command = true;
  /** The state being examined */
  static card_t currentState = CARD_T_MAX;
  /** Visualization level */
  static card_t visual;
  /** Flag: hide/reveal */
  static bool hide;

  /** The printer object */
  extern class Printer thePrinter;
  /** The Petri Net */
  extern class Net* net;
  /** Selected Petri Net */
  extern class Net* subnet;
  /** Selected Petri Net as a path from the root net */
  extern unsigned* modulepath;

  /** Flag: has a complaint been made about a net not being loaded? */
  extern bool notloaded;
  /** Flag: apply modular analysis */
  extern bool modular;
  /** 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 transition that is currently being processed */
  static class Transition* currentTransition = NULL;
  /** The context transition */
  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 an empty list component been parsed? */
  static bool emptyParsed = false;

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

  /** Open a model
   * @param filename	name of the net to be analyzed
   * @param graphfile	base name for the reachability graph files
   * @param regen	flag: regenerate the graph files
   * @return		false on error, true otherwise
   */
  extern bool model (const char* filename, const char* graphfile, bool regen);

  /** Analyze the safety properties of the model
   * @param breadth	flag: search breadth first instead of depth
   * @param expr	the safety formula to analyze (optional)
   * @param visual	visualization level for counterexample
   */
  extern void safety (bool breadth, class Expression* expr, card_t visual);

  /** Generate an LSTS of the model when generating the state space
   * @param filebase	base file name (NULL=disable LSTS generation)
   */
  extern void lsts (const char* filebase);

  /** Unfold the model
   * @param ffilename	output format and file name (NULL=default,stdout)
   */
  extern void unfold (const char* ffilename);

  /** Dump the syntax tree of the model
   * @param visual	visualization level
   */
  extern void dump (card_t visual);

  /** Dump the reachability graph of the model
   * @param visual	visualization level
   */
  extern void dumpgraph (card_t visual);

  /** Perform exhaustive reachability analysis
   * @param state	state to start the analysis with
   * @param breadth	flag: go breadth-first instead of depth-first
   */
  extern void analyze (card_t state, bool breadth);

  /** Check if graphless model checking is selected
   * @return	true if purely state-based approach should be applied
   */
  extern bool isGraphless ();

  /** Check if a state number exists
   * @param state	the state
   * @return		true if the state exists
   */
  extern bool isState (card_t state);

  /** Show a state
   * @param state	the state
   * @param visual	visualization level
   */
  extern void show (card_t state, card_t visual);

  /** Show a path specified as a state list
   * @param sl		the state list
   * @param visual	visualization level
   */
  extern void show (const card_t* states, card_t visual);

  /** Show the successors of a state
   * @param state	the state
   * @param seq		flag: fully expand deterministic sequences
   * @param visual	visualization level
   */
  extern void succ (card_t state, bool seq, card_t visual);

  /** Show the predecessors of a state
   * @param state	the state
   * @param seq		flag: fully expand deterministic sequences
   * @param visual	visualization level
   */
  extern void pred (card_t state, bool seq, card_t visual);

  /** Display statistics */
  extern void stats (void);

  /** Display execution times */
  extern void showtimes (void);

  /** Compute strongly connected components
   * @param state	state in which to start the search
   * @param cond	condition for the states to be included in the search
   */
  extern void strong (card_t state, class Expression* cond);
  /** Display non-trivial terminal strongly connected components */
  extern void terminal ();
  /** Display all strongly connected components
   * @param visual	visualization level
   */
  extern void allcomps (card_t visual);
  /** Show the nodes of a strongly connected component fulfilling a condition
   * @param comp	the component
   * @param cond	the condition
   * @param visual	visualization level
   */
  extern void comp_show (card_t comp,
			 class Expression* cond,
			 card_t visual);
  /** Show the successors of a strongly connected component
   * @param comp	the component
   * @param visual	visualization level
   */
  extern void comp_succ (card_t comp, card_t visual);
  /** Show the predecessors of a strongly connected component
   * @param comp	the component
   * @param visual	visualization level
   */
  extern void comp_pred (card_t comp, card_t visual);
  /** Show the shortest path from a state to a strongly connected component
   * @param state	the state
   * @param comp	the component
   * @param pathc	path condition (must hold in every state on the path)
   * @param visual	visualization level
   */
  extern void path_sc (card_t state, card_t comp,
		       class Expression* pathc,
		       card_t visual);
  /** Show the shortest path from a strongly connected component to a state
   * @param comp	the component
   * @param state	the state
   * @param pathc	path condition (must hold in every state on the path)
   * @param visual	visualization level
   */
  extern void path_cs (card_t comp, card_t state,
		       class Expression* pathc,
		       card_t visual);
  /** Show the shortest path from a state to a state
   * @param state	the source state
   * @param dest	the target state
   * @param pathc	path condition (must hold in every state on the path)
   * @param visual	visualization level
   */
  extern void path_ss (card_t state, card_t dest,
		       class Expression* pathc,
		       card_t visual);
  /** Show the shortest path from a state to a state where a condition holds
   * @param state	the source state
   * @param cond	the condition
   * @param pathc	path condition (must hold in every state on the path)
   * @param visual	visualization level
   */
  extern void path_se (card_t state, class Expression& cond,
		       class Expression* pathc,
		       card_t visual);
  /** Show the shortest path from a state where a condition holds to a state
   * @param state	the target state
   * @param cond	the condition
   * @param pathc	path condition (must hold in every state on the path)
   * @param visual	visualization level
   */
  extern void rpath_se (card_t state, class Expression& cond,
			class Expression* pathc,
			card_t visual);
  /** Show the shortest path from a state to a loop state, plus the loop
   * @param state	the source state
   * @param loop	amount and number of states in the loop
   * @param pathc	path condition (must hold in every state on the path)
   * @param visual	visualization level
   */
  extern void path_sl (card_t state,
		       const card_t* loop,
		       class Expression* pathc,
		       card_t visual);

  /** Evaluate a formula in the current state
   * @param state	state in which to evaluate the formula
   * @param expr	formula to be evaluated
   * @param visual	visualization level for counterexample
   */
  extern void eval (card_t state, class Expression* expr, card_t visual);

  /** Fire an anonymous transition
   * @param trans	the anonymous transition
   * @param state	state in which to fire the transition
   * @param visual	visualization level for counterexample
   */
  extern void fireTransition (class Transition& trans,
			      card_t state,
			      card_t visual);

  /** Display a short help message */
  static void help (void);

  /** Confirm the selection of a subnet */
  static void go_subnet (void);

  /** Derive a net from modulepath */
  static const class Net* getSubnet (void);

  /** Determine whether a model has been loaded
   * @return		true if a model has been loaded
   */
  static bool isNet (void);

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

  /** Get a state proposition by name
   * @param name	the proposition name
   * @return		the corresponding proposition, or NULL
   */
  static const class Expression* getProposition (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
   * @return		pointer to a Marking object, or NULL
   */
  static class Marking* ensureMarking (class Expression* expr,
				       const class Type* type);

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

  /** 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 (const 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	marking expression
   * @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);

  /** 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 dewarn (const char* s);

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

%union{
  card_t i;
  card_t* ia;
  bool flag;
  char_t c;
  char* s;
  class Expression* expr;
  class ExpressionList* exprList;
  const class Place* place;
  class Marking* marking;
  const class Type* type;
}

%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
	STATE
	COMP

%token <c>
	CHARACTER

%token <s>
	NAME

%token
	TRUE_ FALSE_ UNDEFINED FATAL EMPTY
	PLACE TRANS IN_ OUT_ GATE

%token	MODEL GRAPH LSTS UNFOLD BREADTH DEPTH
	STRONG PATH TERMINAL COMPONENTS
	CD TRANSLATOR COMPILEDIR VISUAL DUMP DUMPGRAPH
	EVAL HIDE SUBNET SHOW SUCC PRED GO STATS TIME HELP LOG
	FUNCTION PROMPT EXIT

%type <s>
	name
	_opt_name
	name_

%type <flag>
	_list_var_expr/* true if component present */
	_opt_exclam/* true if present */

%type <i>
	state
	state_
	quantifier

%type <ia>
	statelist
	statelist_

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

%type <exprList>
	arg_list
	colon_formula

%type <marking>
	marking
	marking_list

%type <place>
	placename
	placename_

%type <type>
	typereference

%%

script:
	|
	statements
	;

statements:
	';'
	{ command = true; dewarn ("extraneous semicolon"); }
	|
	';'
	{ command = true; dewarn ("extraneous semicolon"); }
	statement
	{ command = true; }
	|
	statement
	{ command = true; }
	|
	statements ';' statement
	{ command = true; }
	|
	statements ';'
	;

statement:
	MODEL name
	{
	  if ($2) {
	    currentState = model ($2, 0, true) ? 0 : CARD_T_MAX;
	    delete[] $2;
	  }
	}
	|
	GRAPH name
	{
	  if ($2) {
	    currentState = model (0, $2, false) ? 0 : CARD_T_MAX;
	    delete[] $2;
	  }
	}
	|
	analyze
	|
	strong
	|
	_opt_visual eval
	|
	_opt_visual EVAL eval
	|
	_opt_visual EVAL
	{ eval (currentState, resolveName (newString ("eval")), visual); }
	|
	_opt_visual SHOW
	{ show (currentState, visual); }
	|
	_opt_visual SHOW statelist
	{ if ($3) { show ($3, visual); delete[] $3; } }
	|
	_opt_visual SHOW COMP
	{ comp_show ($3, 0, visual); }
	|
	_opt_visual SHOW COMP expr
	{ if (($4 = ensureBool ($4))) comp_show ($3, $4, visual); }
	|
	_opt_visual COMP
	{ comp_show ($2, 0, visual); }
	|
	_opt_visual COMP expr
	{ if (($3 = ensureBool ($3))) comp_show ($2, $3, visual); }
	|
	_opt_visual SUCC _opt_exclam state_
	{ succ ($4, $3, visual); }
	|
	_opt_visual SUCC COMP
	{ comp_succ ($3, visual); }
	|
	_opt_visual PRED _opt_exclam state_
	{ pred ($4, $3, visual); }
	|
	_opt_visual PRED COMP
	{ comp_pred ($3, visual); }
	|
	hide
	|
	subnet
	|
	go
	|
	STATS
	{ stats (); }
	|
	TIME
	{ showtimes (); }
	|
	LOG _opt_name
	{
	  FILE* f = thePrinter.getOutput ();
	  if (f != stderr)
	    fclose (f);
	  if (!$2)
	    f = stderr;
	  else if (!(f = fopen ($2, "w+")))
	    perror ($2), f = stderr;
	  thePrinter.setOutput (f);
	  delete[] $2;
	}
	|
	HELP
	{ help (); }
	|
	path
	|
	FUNCTION function
	|
	_opt_visual TRANS
	{ currentTransition = isNet () ? new class Transition () : 0; }
	trans
	{
	  if (currentTransition) {
	    fireTransition (*currentTransition, currentState, visual);
	    delete currentTransition; currentTransition = 0;
	  }
	}
	|
	_opt_visual STATE TRANS
	{ currentTransition = isState ($2) ? new class Transition () : 0; }
	trans
	{
	  if (currentTransition) {
	    fireTransition (*currentTransition, currentState, visual);
	    delete currentTransition; currentTransition = 0;
	  }
	}
	|
	PROMPT
	{ promptchar = 0; }
	|
	PROMPT expr
	{
	  if (!$2);
	  else if ($2->getKind () != Expression::eConstant ||
		   $2->getType ()->getKind () != Type::tChar)
	    deerror ("prompt: character constant expected");
	  else
	    promptchar = char (char_t (static_cast<const class LeafValue&>
				       (static_cast<class Constant*>
					($2)->getValue ())));
	  $2->destroy ();
	}
	|
	EXIT
	{ exiting = true; YYABORT; }
	;

statelist:
	STATE
	{ $$ = new card_t[2]; $$[0] = 1; $$[1] = $1; }
	|
	statelist STATE
	{
	  if (($$ = $1)) {
	    $$ = new card_t[*$$ + 2];
	    memcpy ($$, $1, (1 + *$1) * sizeof *$1);
	    delete[] $1;
	    $$[++(*$$)] = $2;
	  }
	}
	|
	statelist error
	{ delete[] $1; $$ = 0; }
	;

statelist_:
	STATE
	{ $$ = new card_t[2]; $$[0] = 1; $$[1] = $1; }
	|
	statelist_ STATE
	{
	  if (($$ = $1)) {
	    $$ = new card_t[*$$ + 2];
	    memcpy ($$, $1, (1 + *$1) * sizeof *$1);
	    delete[] $1;
	    $$[++(*$$)] = $2;
	  }
	}
	;

_opt_visual:
	{ visual = 0; }
	|
	_opt_visual VISUAL
	{ visual++; }
	;

analyze:
	LSTS _opt_name
	{ lsts ($2); delete[] $2; }
	|
	UNFOLD _opt_name
	{ unfold ($2); delete[] $2; }
	|
	_opt_visual DUMP
	{ dump (visual); }
	|
	_opt_visual DUMPGRAPH
	{ dumpgraph (visual); }
	|
	_opt_visual BREADTH state_
	{
	  if (!$3 && isGraphless ())
	    safety (true, 0, visual);
	  else if (visual) {
	    thePrinter.printRaw ("ignoring `visual' in graph generation");
	    thePrinter.finish ();
	    analyze ($3, true);
	  }
	  else
	    analyze ($3, true);
	}
	|
	_opt_visual DEPTH state_
	{
	  if (!$3 && isGraphless ())
	    safety (false, 0, visual);
	  else if (visual) {
	    thePrinter.printRaw ("ignoring `visual' in graph generation");
	    thePrinter.finish ();
	    analyze ($3, false);
	  }
	  else
	    analyze ($3, false);
	}
	|
	_opt_visual BREADTH formula
	{
	  if (($3 = ensureBool ($3))) {
	    currentState = CARD_T_MAX;
	    safety (true, $3, visual);
	  }
	}
	|
	_opt_visual DEPTH formula
	{
	  if (($3 = ensureBool ($3))) {
	    currentState = CARD_T_MAX;
	    safety (false, $3, visual);
	  }
	}
	|
	CD _opt_name
	{
	  if (!$2 && ($2 = getenv ("HOME"))) $2 = newString ($2);
	  if ($2 && chdir ($2)) perror ($2); delete[] $2;
	}
	|
	TRANSLATOR _opt_name
	{
#ifdef BUILTIN_LTL
	  if ($2) deerror ("the LTL translator has been built in");
	  delete[] $2;
#else // BUILTIN_LTL
	  delete[] translator;
	  translator = $2;
#endif // BUILTIN_LTL
	}
	|
	COMPILEDIR _opt_name
	{
#ifdef EXPR_COMPILE
	  delete[] compiledir; compiledir = $2;
#else // EXPR_COMPILE
	  delete[] $2; deerror ("no support for code generator");
#endif // EXPR_COMPILE
	}
	;

strong:
	STRONG
	{ strong (currentState, 0); }
	|
	STRONG expr
	{ if (($2 = ensureBool ($2))) strong (currentState, $2); }
	|
	STRONG STATE
	{ strong ($2, 0); }
	|
	STRONG STATE expr
	{ if (($3 = ensureBool ($3))) strong ($2, $3); }
	|
	STRONG expr STATE
	{ if (($2 = ensureBool ($2))) strong ($3, $2); }
	|
	TERMINAL
	{ terminal (); }
	|
	_opt_visual COMPONENTS
	{ allcomps (visual); }
	;

eval:
	STATE formula
	{ eval ($1, $2, visual); }
	|
	formula
	{ eval (currentState, $1, visual); }
	;

state_:
	{ $$ = currentState; }
	|
	state_ STATE
	{
	  if (($$ = $1) != currentState) {
	    thePrinter.printRaw ("ignoring extraneous state qualifier ");
	    thePrinter.printState ($2);
	    thePrinter.finish ();
	  }
	  else
	    $$ = $2;
	}
	;

state:
	{ $$ = currentState; }
	|
	state STATE
	{
	  if (($$ = $1) != currentState) {
	    thePrinter.printRaw ("ignoring extraneous state qualifier ");
	    thePrinter.printState ($2);
	    thePrinter.finish ();
	  }
	  else
	    $$ = $2;
	}
	|
	state COMP
	{ deerror ("expecting a state number"); $$ = $1; }
	|
	state name_
	{
	  deerror ("expecting a state number");
	  delete[] $2; $$ = $1;
	}
	;

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

_opt_place:
	|
	PLACE
	;

hide:
	HIDE _opt_exclam
	{
	  if (isNet ())
	    for (unsigned place = subnet->getNumPlaces (); place--; )
	      if (const class Place* p = subnet->getPlace (place))
		p->setSuppressed (hide = !$2);
	}
	|
	HIDE _opt_exclam
	{ if (isNet ()) hide = !$2; }
	hide_list
	;

hide_list:
	_opt_place name_
	{
	  if (subnet && $2) {
	    if (const class Place* p = subnet->getPlace ($2))
	      p->setSuppressed (hide);
	    else {
	      thePrinter.printRaw ("undefined place ");
	      thePrinter.printQuoted ($2);
	      thePrinter.finish ();
	    }
	  }
	  delete[] $2;
	}
	|
	hide_list ',' _opt_place name
	{
	  if (subnet && $4) {
	    if (const class Place* p = subnet->getPlace ($4))
	      p->setSuppressed (hide);
	    else {
	      thePrinter.printRaw ("undefined place ");
	      thePrinter.printQuoted ($4);
	      thePrinter.finish ();
	    }
	  }
	  delete[] $4;
	}
	;

subnet:
	SUBNET
	{ delete[] modulepath; modulepath = 0; subnet = net; }
	|
	SUBNET '/'
	{ delete[] modulepath; modulepath = 0; subnet = net; }
	|
	SUBNET '/'
	{
	  for (subnet = net; subnet && subnet->getParent (); )
	    subnet = subnet->getParent ();
	}
	subnet_list
	{ go_subnet (); }
	|
	SUBNET
	subnet_list
	{ go_subnet (); }
	;

subnet_list:
	subnet_elem
	|
	subnet_list '/' subnet_elem_
	;

subnet_elem_:
	|
	subnet_elem
	;

subnet_elem:
	'.'
	|
	'.' '.'
	{ if (subnet) subnet = subnet->getParent (); }
	|
	NUMBER
	{
	  subnet = subnet && $1 < subnet->getNumChildren ()
	    ? &const_cast<class Net&>(subnet->getChild ($1))
	    : 0;
	}
	|
	name_
	{
	  if (subnet && $1) {
	    const class Net* parent = subnet;
	    subnet = 0;
	    for (unsigned i = parent->getNumChildren (); !subnet && i--; ) {
	      const class Net* child = &parent->getChild (i);
	      if (child->getName () && !strcmp (child->getName (), $1))
		subnet = const_cast<class Net*>(child);
	    }
	  }
	  else
	    subnet = 0;
	  delete[] $1;
	}
	;

go:
	GO state
	{ if (isState ($2)) currentState = $2; }
	|
	_opt_visual STATE
	{
	  if (visual) {
	    thePrinter.printRaw ("ignoring extraneous `visual'");
	    thePrinter.finish ();
	  }
	  if (isState ($2)) currentState = $2;
	}
	;

path:
	_opt_visual PATH COMP state path_expr
	{ path_sc ($4, $3, $5, visual); }
	|
	_opt_visual PATH STATE COMP path_expr
	{ path_cs ($4, $3, $5, visual); }
	|
	_opt_visual PATH statelist_ path_expr
	{
	  if (!$3 || !*$3)
	    $4->destroy ();
	  else if (*$3 <= 2) {
	    card_t src = *$3 >= 2 ? $3[2] : currentState, dest = $3[1];
	    path_ss (src, dest, $4, visual);
	  }
	  else
	    path_sl (currentState, $3, $4, visual);
	  delete[] $3;
	}
	|
	_opt_visual PATH expr path_expr
	{
	  if (($3 = ensureBool ($3)))
	    path_se (currentState, *$3, $4, visual);
	}
	|
	_opt_visual PATH expr STATE path_expr
	{ if (($3 = ensureBool ($3))) path_se ($4, *$3, $5, visual); }
	|
	_opt_visual PATH STATE expr path_expr
	{ if (($4 = ensureBool ($4))) rpath_se ($3, *$4, $5, visual); }
	;

path_expr:
	{ $$ = NULL; }
	|
	path_expr ',' expr_
	{
	  if (($$ = $1)) {
	    deerror ("ignoring extraneous path condition");
	    $3->destroy ();
	  }
	  else
	    $$ = ensureBool ($3);
	}
	;
typereference:
	name_
	{
	  if (!$1 || !isNet ()) $$ = NULL;
	  else if (($$ = subnet->getType ($1)));
	  else if (!strcmp ($1, "bool"))
	    $$ = &Net::getBoolType ();
	  else if (!strcmp ($1, "int"))
	    $$ = &Net::getIntType ();
	  else if (!strcmp ($1, "unsigned"))
	    $$ = &Net::getCardType ();
	  else if (!strcmp ($1, "char"))
	    $$ = &Net::getCharType ();
	  else {
	    thePrinter.printRaw ("undefined type ");
	    thePrinter.printQuoted ($1);
	    thePrinter.finish ();
	  }
	  delete[] $1;
	}
	;

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

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

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

delim:	';' | ',' ;

next_eq:
	NEXT
	|
	'='
	;

function:
	typereference name next_eq
	{ currentType = $1; }
	formula
	{ defun ($1, $2, $5); }
	|
	typereference name '(' param_list ')'
	{
	  currentType = $1;
	  if (emptyParsed && currentArity)
	    deerror ("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)
	    deerror ("extraneous delimiter in parameter list");
	  addParameter ($3, $4);
	  emptyParsed = false;
	}
	|
	param_list delim
	{
	  if (emptyParsed)
	    deerror ("extraneous delimiter in parameter list");
	  emptyParsed = true;
	}
	|
	{ if (!currentArity && !currentParameters) emptyParsed = true; }
	;

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; }
	;

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

var_expr:
	/* to do: add local functions? */
	typereference name
	{
	  if ($1 && $2 && currentTransition) {
	    if (currentTransition->getVariable ($2)) {
	      thePrinter.printRaw ("redefinition of variable ");
	      thePrinter.printQuoted ($2);
	      thePrinter.finish ();
	      delete[] $2;
	    }
	    else
	      currentTransition->addVariable ($2, *$1, false, false);
	  }
	  else
	    delete[] $2;
	}
	|
	typereference name_ '!'
	{ beginAny ($1, $2, false); }
	_opt_expr %prec ':'
	{ endAny ($1, $2, $5)->destroy (); }
	;

trans_places:
	'{' _list_place_marking '}'
	;

_list_place_marking:
	place_marking
	|
	_list_place_marking ';' place_marking
	;

place_marking:
	|
	_opt_place placename_ ':'
	{ currentType = $2 ? &$2->getType () : NULL; }
	marking_list
	{
	  if (!$2 || !currentTransition)
	    $5->destroy ();
	  else if ($5) {
	    if (class Arc* arc =
		currentTransition->getArc (isOutputArc, *$2))
	      arc->append (*$5);
	    else
	      currentTransition->addArc (*new class Arc
					 (isOutputArc, *$5, *$2));
	  }
	  currentType = NULL;
	}
	;

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))) {
		dewarn ("gate is constantly enabled");
		$1->destroy ();
	      }
	      else {
		dewarn ("gate is constantly disabled");
		currentTransition->addGate (*$1);
	      }
	    }
	    else
	      currentTransition->addGate (*$1);
	  }
	  else
	    $1->destroy ();
	}
	;

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) {
		  deerror ("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 ();
		    $$->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 ();
		  $$->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 ();
		    $$->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 ();
		  $$->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; deerror ("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 ();
	      }
	    }
	    else {
	      thePrinter.printRaw ("there is no nullary function ");
	      thePrinter.printQuoted ($1);
	      thePrinter.finish ();
	    }
	  }
	}
	|
	name_ '('
	{
	  class Function* f = NULL;
	  if ($1 && !(f = getFunction ($1))) {
	    thePrinter.printRaw ("there is no function ");
	    thePrinter.printQuoted ($1);
	    thePrinter.finish ();
	  }
	  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 ();
	    }
	  }
	  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 ();
	}
	|
	placename
	{ $$ = $1 ? (new class PlaceContents (*$1))->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) {
	    deerror ("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 (dewarnings) {
		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:
		  dewarn ("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:
	      deerror ("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 ();
		    $$->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 ();

		    $$->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 ()) {
	      deerror ("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 ();
	    }
	  }
	  else if ($1)
	    deerror ("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 ();

	      $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 ();

		$1->destroy (), $$ = NULL;
	      }
	    }
	    else {
	      deerror ("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) {
	      deerror ("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 ()) {
	      deerror ("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 ();
	      $1->destroy (); $3->destroy (); $$ = NULL;
	    }
	    else if ($1->getType ()->getNumValues () == 1) {
	      dewarn ("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) {
	      deerror ("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) {
	      deerror ("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 ();

	      $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 ();

		$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 ();

		$1->destroy (), $$ = NULL;
	      }
	    }
	    else {
	      deerror ("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)
		deerror ("a buffer index has already been specified");
	      break;
	    default:
	      deerror ("only vector-typed expressions can be indexed");
	      $1->destroy (), $1 = NULL;
	    }
	  }
	  else if ($1) {
	    deerror ("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) {
		deerror ("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); }
	|
	'.' name_ _opt_name
	{ $$ = quantifierVariable ($2, $3, false); }
	|
	':' name_ _opt_name
	{ $$ = quantifierVariable ($2, $3, true); }
	|
	SUBSET name '{' marking_list_ '}'
	{
	  if ($4 && $4->getKind () == Expression::eEmptySet) {
	    dewarn ("any subset of an empty set is an empty set");
	    delete[] $2, $2 = NULL;
	  }
	  else if (($4 = ensureSet ($4)) && $2 && $4->getType ())
	    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 (dewarnings) {
	      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 ();
	      dewarn (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) {
	    dewarn ("any subset of an empty set is an empty set");
	    delete[] $2, $2 = NULL;
	  }
	  else if (($4 = ensureSet ($4)) && $2 && $4->getType ())
	    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 (dewarnings) {
	      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) {
	    dewarn ("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 ()) {
	    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 (dewarnings) {
		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 ();
	      $$ = 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 ();
	    }
	  }

	  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 ();
	      }
	    }
	  }

	  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)) deerror ("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 ()) {
	      deerror ("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;
	}
	;

placename_:
	name
	{
	  if ($1 && isNet ()) {
	    if (!($$ = subnet->getPlace ($1))) {
	      thePrinter.printRaw ("undefined place ");
	      thePrinter.printQuoted ($1);
	      thePrinter.finish ();
	    }
	  }
	  else
	    $$ = NULL;
	  delete[] $1;
	}
	;

placename:
	_nopt_place placename_
	{ $$ = $2; }
	;

_nopt_place:
	PLACE
	|
	_nopt_place PLACE
	{ deerror ("ignoring extraneous `place'"); }
	;

%%

#include "cmdline.h"

const char*
deline (void)
{
  static class StringBuffer prompt1, prompt2;
  prompt1.create (0);
  class Printer printer;
  printer.setOutput (&prompt1);
  assert (subnet == getSubnet ());

  if (modulepath) {
    const class Net* n = net;
    for (unsigned i = 1; i <= *modulepath; ) {
      n = &n->getChild (modulepath[i]);
      if (n->getName ())
	printer.print (n->getName ());
      else
	printer.print (modulepath[i]);
      if (++i <= *modulepath)
	printer.delimiter ('/');
    }
    assert (n == subnet);
  }

  /* print the state number */
  if (currentState != CARD_T_MAX)
    printer.printState (currentState);
  /* copy prompt1 to prompt2 */
  memcpy (prompt2.create (prompt1.getLength ()), prompt1.getString (),
	  prompt1.getLength ());
  printer.delimiter ('$'); // prompt1 terminator
  printer.setOutput (&prompt2);
  printer.delimiter ('>'); // prompt2 terminator
  extern volatile bool interrupted;
  if (interrupted)
    fputs ("[Interrupt]\n", stderr);
  interrupted = true;
  if (promptchar)
    putc (promptchar, stderr), putc ('\n', stderr);
  const char* line = cmdline (prompt1.getString (), prompt2.getString ());
  interrupted = false;
  return line;
}

#ifdef HAS_READLINE
/** Duplicate a string
 * @param s	string to be duplicated
 * @return	a copy of the string allocated with malloc ()
 */
inline static char*
dupstr (const char* s)
{
  size_t length = strlen (s) + 1;
  char* r = static_cast<char*>(malloc (length));
  if (r)
    memcpy (r, s, length);
  return r;
}

/** flag: is quoting desired? */
extern int rl_filename_completion_desired;
/** character to be appended to completed words (0 or ' ') */
extern int rl_completion_append_character;
/** Iterate through all file names starting with given text
 * @param text		text to be completed
 * @param state		flag: if state==0, start from the beginning
 * @return		a file name, or NULL if no match
 */
extern "C" char* filename_completion_function (const char* text, int state);

/** Iterate through all keywords starting with given text
 * @param text		text to be completed
 * @param state		flag: if state==0, start from the beginning
 * @return		a command name, or NULL if no match
 */
extern "C" char*
marde_keywords (const char* text, int state)
{
  /** Keywords of the language */
  static const char* keywords[] = {
    "model \"", "graph \"", "breadth", "depth", "strong", "path",
    "terminal", "components", "cd", "translator \"", "compiledir \"",
    "visual", "dump", "dumpgraph", "eval", "hide", "show",
    "succ", "pred", "go", "stats", "time", "help", "log",
    "function", "exit", "prompt", "union", "is", "until",
    "release", "subset", "intersect", "equals", "minus", "atom",
    "empty", "place \"", "true", "false", "undefined", "fatal",
    "cardinality", "min", "max"
  };

  /** Number of the last keyword searched */
  static unsigned i;
  /** Length of the string */
  static size_t length;

  if (!state) {
    i = 0;
    length = strlen (text);
  }
  else {
    assert (i <= (sizeof keywords) / sizeof *keywords);
    assert (length == strlen (text));
  }

  while (i < (sizeof keywords) / sizeof *keywords) {
    const char* name = keywords[i++];
    if (!strncmp (name, text, length)) {
      if (name[strlen (name) - 1] == '"')
	rl_completion_append_character = 0;
      return dupstr (name);
    }
  }

  return NULL;
}

/** Iterate through place names starting with given text
 * @param text		text to be completed
 * @param state		flag: if state==0, start from the beginning
 * @return		a place name, or NULL if no match
 */
extern "C" char*
marde_placenames (const char* text, int state)
{
  /** Number of the last place searched */
  static unsigned place;
  /** Length of the string */
  static size_t length;

  if (!state) {
    place = 0;
    length = strlen (text);
    rl_filename_completion_desired = 1;
  }
  else {
    assert (subnet && place <= subnet->getNumPlaces ());
    assert (length == strlen (text));
    assert (rl_filename_completion_desired);
  }

  if (!subnet)
    return NULL;

  while (place < subnet->getNumPlaces ())
    if (const class Place* p = subnet->getPlace (place++))
      if (!strncmp (p->getName (), text, length))
	return dupstr (p->getName ());

  return NULL;
}

/** Iterate through type names starting with given text
 * @param text		text to be completed
 * @param state		flag: if state==0, start from the beginning
 * @return		a type name, or NULL if no match
 */
extern "C" char*
marde_typenames (const char* text, int state)
{
  /** Last type searched */
  static Net::TypeMap::const_iterator type;
  /** Length of the string */
  static size_t length;

  if (!subnet)
    return NULL;

  if (!state) {
    type = subnet->beginTypename ();
    length = strlen (text);
    rl_filename_completion_desired = 1;
  }
  else {
    assert (length == strlen (text));
    assert (rl_filename_completion_desired);
  }

  while (type != subnet->endTypename ()) {
    const char* name = (type++)->first;
    if (!strncmp (name, text, length))
      return dupstr (name);
  }

  return NULL;
}

/** Iterate through other names starting with given text
 * @param text		text to be completed
 * @param state		flag: if state==0, start from the beginning
 * @return		a name
 */
extern "C" char*
marde_othernames (const char*, int)
{
  return NULL;
}

/** Completion function of readline
 * @param word		word to be completed
 * @param completer	the completion function
 */
extern "C" char **
completion_matches (const char* word,
		    char* (*completer) (const char*, int));

/** Complete place or type names
 * @param word		word to be completed
 * @param start		start offset of the string to be completed
 * @param end		end offset of the string to be completed
 */
extern "C" char**
marde_completer (const char* word, int start, int end)
{
  /** The line gathered so far */
  extern char* rl_line_buffer;
  assert (start >= 0 && start <= end);
  assert (size_t (end - start) == strlen (word));
  assert (size_t (end) <= strlen (rl_line_buffer));
  rl_completion_append_character = ' ';

  if ((start >= 6 && !memcmp (&rl_line_buffer[start - 6], "place ", 6)) ||
      (start >= 7 && !memcmp (&rl_line_buffer[start - 7], "place \"", 7)))
    return completion_matches (word, marde_placenames);
  if ((start >= 3 && !memcmp (&rl_line_buffer[start - 3], "is ", 3)) ||
      (start >= 4 && !memcmp (&rl_line_buffer[start - 4], "is \"", 4)))
    return completion_matches (word, marde_typenames);
  if ((start >= 3 && !memcmp (&rl_line_buffer[start - 3], "cd ", 3)) ||
      (start >= 4 && (!memcmp (&rl_line_buffer[start - 4], "cd \"", 4) ||
		      !memcmp (&rl_line_buffer[start - 4], "log ", 4))) ||
      (start >= 5 && !memcmp (&rl_line_buffer[start - 5], "log \"", 5)) ||
      (start >= 6 && (!memcmp (&rl_line_buffer[start - 6], "model ", 6) ||
		      !memcmp (&rl_line_buffer[start - 6], "graph ", 6))) ||
      (start >= 7 && (!memcmp (&rl_line_buffer[start - 7], "model \"", 7) ||
		      !memcmp (&rl_line_buffer[start - 7], "graph \"", 7) ||
		      !memcmp (&rl_line_buffer[start - 7], "depth \"", 7))) ||
      (start >= 9 && (!memcmp (&rl_line_buffer[start - 9],
			       "breadth \"", 9))) ||
      (start >= 11 && (!memcmp (&rl_line_buffer[start - 11],
				"translator ", 11) ||
		       !memcmp (&rl_line_buffer[start - 11],
				"compiledir ", 11))) ||
      (start >= 12 && (!memcmp (&rl_line_buffer[start - 12],
				"translator \"", 12) ||
		       !memcmp (&rl_line_buffer[start - 12],
				"compiledir \"", 12))))
    return completion_matches (word, filename_completion_function);
  return completion_matches (word, marde_keywords);
}
#endif // HAS_READLINE

void
dereset ()
{
  command = true;
  delete[] currentParameters;
  currentParameters = 0;
  while (!functionStack.empty ()) functionStack.pop ();
  currentContextTransition = 0;
  currentType = currentContextType = 0;
  currentComponent = 0;
  while (!typeStack.empty ()) typeStack.pop ();
  emptyParsed = false;
  while (!structLevels.empty ()) structLevels.pop ();
  if (currentState == CARD_T_MAX) currentState = 0;
  delexreset ();
#ifdef HAS_READLINE
  init_cmdline ("marde", marde_completer, marde_othernames);
#endif // HAS_READLINE
}

static void
help (void)
{
  fputs ("Query language commands:\n"
	 "\tmodel \"MODELNAME\"\n"
	 "\tgraph \"GRAPHFILE\"\n"
	 "\t(breadth|depth) [STATE]\n"
	 "\t(breadth|depth) \"MODELNAME\"\n"
	 "\tlsts [\"OUTFILE\"]\n"
	 "\tunfold M?([lmpr](OUTFILE)?)?\n"
	 "\thide [!][[place] PLACENAME (, [place] PLACENAME)*]\n"
	 "\t[visual] [STATE] [eval] formula\n"
	 "\t[visual] show [STATE]\n"
	 "\t[visual] [show] COMP [expr]\n"
	 "\t[visual] (succ|pred)[!] [STATE]\n"
	 "\t[visual] (succ|pred) COMP\n"
	 "\t[go] STATE\n"
	 "\tstrong [STATE] [expr]\n"
	 "\tterminal\n"
	 "\t[visual] components\n"
	 "\t[visual] path (STATE|COMP|expr) [STATE]\n"
	 "\t[visual] path STATE (COMP|expr)\n"
	 "\t[visual] [STATE] trans {transition-definition}\n"
	 "\t[visual] dump[graph]\n"
	 "\tsubnet [[(..|NAME|NUMBER)] (/ (..|NAME|NUMBER))*]\n"
	 "\tstats\n"
	 "\ttime\n"
	 "\tcd \"DIRECTORY\"\n"
	 "\ttranslator [\"COMMAND\"]\n"
	 "\tcompiledir [\"DIRECTORY\"]\n"
	 "\thelp\n"
	 "\tfunction {function-definition}\n"
	 "\tlog [\"LOGFILE\"]\n"
	 "\tprompt ['c']\n"
	 "\texit\n",
	 stderr);
}

static void
go_subnet (void)
{
  if (!net) {
    if (!notloaded) {
      notloaded = true;
      deerror ("no model has been loaded");
    }
  }
  else if (!subnet && net)
    deerror ("no such net");
  else if (!modular)
    deerror ("modular analysis not specified");
  else {
    /** distance from the root net */
    unsigned moddepth;
    unsigned i;
    const class Net* n;
    for (moddepth = 0, n = subnet; (n = n->getParent ()); moddepth++);
    delete[] modulepath; modulepath = 0;
    if (moddepth) {
      /** array of module path components */
      *(modulepath = new unsigned[moddepth + 1]) = moddepth;
      for (n = subnet, i = moddepth + 1; --i; n = n->getParent ()) {
	const class Net* parent = n->getParent ();
	for (unsigned child = parent->getNumChildren (); child--; ) {
	  if (&parent->getChild (child) == n) {
	    modulepath[i] = child;
	    goto found;
	  }
	}
	assert (false);
      found:
	continue;
      }
      assert (n == net);
    }
    currentState = 0;
    return;
  }
  /* restore the previously selected subnet */
  subnet = const_cast<class Net*>(getSubnet ());
}

static const class Net*
getSubnet (void)
{
  const class Net* n = net;
  if (modulepath)
    for (unsigned i = 1; i <= *modulepath; i++)
      n = &n->getChild (modulepath[i]);
  return n;
}

static bool
isNet (void)
{
  assert (subnet == getSubnet ());
  if (net)
    return true;
  if (!notloaded) {
    notloaded = true;
    deerror ("no model has been loaded");
  }
  return false;
}

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 ();
    delete[] name;
    return;
  }

  if (dewarnings) {
    const char* shadow = 0;
    if (getConstant (name) || getConstantFunction (name))
      shadow = " shadows a constant";
    else if (subnet->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 class Function*
getFunction (const char* name)
{
  if (!isNet ())
    return NULL;
  class Function* f = currentContextTransition
    ? currentContextTransition->getFunction (name)
    : NULL;
  return f ? f : subnet->getFunction (name);
}

static class Function*
getConstantFunction (const char* name)
{
  if (!isNet ())
    return NULL;
  class Function* f = currentContextTransition
    ? currentContextTransition->getFunction (name)
    : NULL;
  if (!f || f->getArity ())
    f = subnet->getFunction (name);
  return f && f->getArity () ? NULL : f;
}

static const class Constant*
getConstant (const char* name)
{
  if (!isNet ())
    return NULL;
  const class Constant* c = currentContextTransition
    ? currentContextTransition->getConstant (name)
    : NULL;
  return c ? c : subnet->getConstant (name);
}

static const class Expression*
getProposition (char* name)
{
  if (subnet)
    for (unsigned i = subnet->getNumProps (); i--; )
      if (!strcmp (subnet->getPropName (i), name))
	return &subnet->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 ();
    }
    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 ();
    delete[] name;
  }
  else {
    assert (currentType != NULL);

    if (dewarnings && subnet->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))
      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);
  if (i)
    return enumValue (*static_cast<const class EnumType*>(currentType), *i);
  if (prop)
    return (const_cast<class Expression*>(prop)->copy ())->cse ();

  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)
{
  if (!expr)
    return NULL;
  else if (expr->getKind () == Expression::eUndefined) {
    deerror ("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 (!type || makeCompatible (expr, *type)) {
      class Marking* m = new class Marking (NULL);
      m->setToken (expr);
      return m;
    }
    else
      return NULL;
  }
  else {
    deerror ("marking expression expected");
    expr->destroy ();
    return NULL;
  }
}

static class Expression*
noMarking (class Expression* expr)
{
  if (expr && expr->isSet ()) {
    deerror ("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 ()) {
    deerror ("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;
    deerror ("undefined or fatal expression is not meaningful here");
    expr->destroy (); return NULL;
  }

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

  if (expr->getType ()->getKind () != Type::tBool) {
    deerror ("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 ()) {
    deerror ("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:
    deerror ("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 ()) {
    deerror ("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:
    deerror ("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 ()) {
    deerror ("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:
    deerror ("integer expression expected");
    expr->destroy ();
    return NULL;
  }
}

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

  if (!expr)
    return NULL;

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

  if (expr->getType ()->getKind () != Type::tBuffer) {
    deerror ("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 ()) {
    deerror ("expression has no type");
    expr->destroy (); return NULL;
  }

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

  return expr;
}

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 ();
    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 ();
    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 ();
      expr->destroy ();
      delete[] name;
      return NULL;
    }
  }
  else {
    thePrinter.printRaw ("extraneous named component ");
    thePrinter.printQuoted (name);
    thePrinter.printRaw (" at end of struct");
    thePrinter.finish ();
    expr->destroy ();
    delete[] name;
    return NULL;
  }

  delete[] name;
  return expr;
}

static void
beginAny (const class Type*& type, char* name,
	  bool hidden)
{
  if (type) {
    if (currentTransition) {
      if (name && currentTransition->getVariable (name)) {
	thePrinter.printRaw ("output variable ");
	thePrinter.printQuoted (name);
	thePrinter.printRaw (" shadows another variable");
	thePrinter.finish ();
	type = NULL; delete[] name;
      }
      else {
	if (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 ();
	}
      }
    }
    else {
      deerror ("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 (dewarnings && 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) {
    if (dewarnings) {
      const char* shadow = 0;
      if (currentVariables.find (name))
        shadow = " shadows another quantifier";
      else if ((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 (subnet->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 ();
    }
    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)
{
  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))) && dewarnings) {
      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)) {
	expr = m->quantify (v, currentContextTransition,
			    *var, condition, false);
	m->destroy ();
	condition->destroy ();
	delete var;
	if (!expr) {
	  if (check (v))
	    deerror ("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, currentContextTransition,
				  *var, condition, false);
	expr->destroy ();
	delete var;
	if (!e) {
	  if (check (v))
	    deerror ("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 ();
  }
  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 ();
  }
  else if (!currentContextTransition) {
    thePrinter.printRaw ("quantifier ");
    thePrinter.print (var->getName ());
    thePrinter.printRaw (": cannot define ");
    thePrinter.delimiter (delimiter);
    thePrinter.print (prefix);
    thePrinter.printRaw (" outside transition");
    thePrinter.finish ();
  }
  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 ();
    }
  }
  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 ();
  }

  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 && dewarnings &&
	((!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 ();
      return false;
    }
    return true;
  }
  else {
    thePrinter.printRaw ("cannot convert from ");
    printType (srcType);
    thePrinter.printRaw (" to ");
    printType (type);
    thePrinter.finish ();
    return false;
  }
}

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

  if (!expr)
    return false;

  if (!expr->getType ()) {
    deerror ("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 ();
    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 ();
    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 ();
    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 ();
    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 ();
    return false;
  }

  return true;
}

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

  switch (v.getError ()) {
  case errDiv0:
    ok = false, deerror ("division by zero"); break;
  case errOver:
    ok = false, deerror ("overflow"); break;
  case errMod:
    ok = false, deerror ("modulus error"); break;
  case errShift:
    ok = false, deerror ("shift error"); break;
  case errConst:
    ok = false, deerror ("constraint violation"); break;
  case errUnion:
    ok = false, deerror ("union violation"); break;
  case errBuf:
    ok = false, deerror ("buffer violation"); break;
  case errCard:
    ok = false, deerror ("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 (type && name && expr) {
    if (subnet->getFunction (name)) {
      thePrinter.printRaw ("ignoring redefinition of function ");
      thePrinter.printQuoted (name);
      thePrinter.finish ();
      delete[] name, expr->destroy ();
    }
    else if (isCompatible (expr, type)) {
      class Function* f =
	new class Function (name, currentArity, currentParameters, expr);
      subnet->addFunction (*f);

      if (dewarnings && subnet->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;
    deerror ("expressions in comparison have different types");
  }
  else if (!left->getType ()->isOrdered () && !equal)
    deerror ("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
    deerror ("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 ();
      dewarn ("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) {
	dewarn ("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));
}
