/*
   File: rtsagfl.c
   Defines the interpreter for the instructions generated by agfl-coder.

   Copyright 2005-2006 Radboud University of Nijmegen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Library General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   CVS ID: "$Id: rtsagfl.c,v 1.141 2006/10/18 12:03:25 marcs Exp $"
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

/* standard includes */
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

/* libabase includes */
#include <abase_error.h>
#include <abase_memalloc.h>
#include <abase_mm_alloc.h>

/* liblexicon include */
#include <lexicon.h>

/* local includes */
#include "rtsopt.h"
#include "rtscode.h"
#include "rtslex.h"
#include "posmemo.h"
#include "rtsio.h"
#include "rtsagfl.h"
#include "rtstime.h"

#ifdef GEN_RTS
#include "rtsgen.h"
#endif /* GEN_RTS */

#ifdef PMRTS
#include "posmemo.h"
#endif /* PMRTS */

#ifdef DEBUG_RTS
#define DB_RTS(x) x
#else
#define DB_RTS(x)
#endif /* DEBUG_RTS */

#ifdef DEBUG_INTERPRETER
#  undef DEBUG_TRANSLATE
#  define TRACE_MATCH
#  undef TRACE_LEX_MATCH
#endif /* DEBUG_INTERPRETER */

/*------------------------------------------------------------------------------
// Cells
//
// Definition of data/code/cel/union Cel moved here from rtscode.h
// A cell should fit in one machine word.
//----------------------------------------------------------------------------*/
typedef union Cel CODE;
typedef union Cel DATA;
typedef union Cel
{   void	*action;
    char	*str;
    ARG		arg;
    Value	val;
    CODE	*code;
    DATA	*data;
    StateIndicator input_state;	 /* these 3 not in generated code */
    Transition	*input_transition;
    PosMemo	pmprod;
    Penalty	penalty;
    int		ival;
} cel;

extern CODE root;		/* start label of code to be interpreted */

/*------------------------------------------------------------------------------
// Penalty management
// Penalties are defined to be 32 bit integers
//----------------------------------------------------------------------------*/
static int penalty_change_would_block (Penalty current, Penalty offset)
{ if (offset == MAX_PENALTY) return (1);
  else if ((offset > 0) && (current > MAX_PENALTY - offset)) return (1);
  else if ((offset < 0) && (current < MIN_PENALTY - offset))
    abs_abort ("penalty_change_would_block",
	       "Current parse has gathered too much bonusses to be true");
  return (0);
}

/*------------------------------------------------------------------------------
// Private globals
//----------------------------------------------------------------------------*/
typedef struct
{ int		nr_parses;	/* nr of parses */
  Penalty	penalty;	/* penalty of last parse */
  Position	startpos;	/* start position in current input buffer */
  Position	endpos;		/* end of the text covered by the parse */
} ParseResult;

static ParseResult parse_result;
static void reset_parse_result ()
{ parse_result.nr_parses = 0;
  parse_result.penalty = 0;
  parse_result.startpos = 0;
  parse_result.endpos = 0;
}

static void incr_nr_parses ()
{ parse_result.nr_parses++;
}

static int get_nr_parses ()
{ return (parse_result.nr_parses);
}

static Penalty get_penalty ()
{ return (parse_result.penalty);
}

static Position get_parse_startpos ()
{ return (parse_result.startpos);
}

static Position get_parse_endpos ()
{ return (parse_result.endpos);
}

/*------------------------------------------------------------------------------
// Time limit.
//
// The time-out is checked for each succeeding match of a terminal.
// For efficiency reasons, we only check the clock once every
// MAX_TICKS times. High MAX_TICKS increases performance, but
// reduces timer resolution.
//----------------------------------------------------------------------------*/
static int have_time_limit (void)
{ return (max_parsetime < INT_MAX);
}

static void set_time_limit (void)
{ set_time_out (max_parsetime);
}

/*------------------------------------------------------------------------------
// Printing
//----------------------------------------------------------------------------*/
static void print_parsed_sentence (void)
{ if (lexer_stats_option)
    { const char *input_filename = get_input_file_name ();
      int input_linenumber = get_input_linenumber ();
      int input_position = get_input_position ();
      const char *input_text = get_input_text ();
      int parse_start = (int) get_parse_startpos ();
      int parse_end = (int) get_parse_endpos ();
      if (input_filename == NULL) input_filename = "";

      /* Print to stdout? */
      printf ("\n# %s %d %d-%d|%.*s\n", input_filename, input_linenumber,
	      input_position + parse_start, input_position + parse_end,
	      parse_end - parse_start, input_text + parse_start);
    };
}

static void start_printing (void)
{ if (parsing_stats_option)
    { int nr_parses = get_nr_parses ();
      Penalty penalty = get_penalty ();
      Time parse_time = get_parse_time ();

      current_parse_printf ("# parsing %d", nr_parses);
      current_parse_printf (" time %.3f", parse_time);
      current_parse_printf (" penalty %d\n", penalty);
    };
}

static void stop_printing (void)
{ if (!no_output) 
    current_parse_add_char ('\n');
}

static void show_totals (Trellis *trellis)
{ int nr_parses = get_nr_parses ();

  if (total_stats_option)
    { Time scan_time = get_total_scan_time ();
      Time parse_time = get_total_parse_time ();
      Time print_time = get_total_print_time ();
      Time total_time = scan_time + parse_time + print_time;

      abs_printf ("# parsings %d/", nr_parses);
      if (max_parses == INT_MAX) abs_printf ("unlimited");
      else abs_printf ("%d", max_parses);
      abs_printf (" scan %.3f parse %.3f print %.3f total %.3f time\n",
		  scan_time, parse_time, print_time, total_time);
    };

#ifdef DEBUG_NEGMEMO
  if (neg_memo_option)
    print_negmemo_table (trellis, 1);
#endif /* DEBUG_NEGMEMO */

#if defined(PMRTS) && defined(DEBUG_POSMEMO)
  if (pos_memo_option)
    posmemo_dump_table (trellis);
#endif /* DEBUG_POSMEMO */

#ifdef PROFILING
  if ((prof_count > 0) && reset_profile && !no_output)
    ShowProfile ("sentence");
#endif
}

/*------------------------------------------------------------------------------
// Stack frame layout.
//      The interpreter uses two different stack layouts: one for the first
//      pass, and one for the second pass.
//
//      The first pass frame consists of:
//	  - constants pushed by the CALL instruction (the elements with a
//	    positive offset);
//	  - a fixed number of entries filled in by the CREATE instruction (the
//	    elements with a negative offset);
//	  - zero or more formals;
//	  - zero or more locals;
//	  - zero or more entries for pointers to the sons of this rule.
//      The formals are untouched by the CREATE instruction.
//      The locals and sons are cleared to 0 by the CREATE instruction.
//
//      The second pass frame consists of:
//	  - constants pushed by the PRINT_SON instruction
//	  - parameters for the second pass function.
//
//      Both a stack pointer and a frame pointer are used. The execution model
//      allows to continue execution in another frame.
//
//	Note that the SP grows from high to low memory,
//	always pointing to the first free location
//----------------------------------------------------------------------------*/
#define STACK_SIZE		(64 * 1024)
static DATA* stack = 0;

#define PASS2_FRAME_HEAD_LEN	(3)     /* length of the second pass frame * head */
#define PASS2_PASS2FP		(3)     /* previous second pass frame pointer */
#define PASS2_FP		(2)     /* previous rule frame pointer */
#define PASS2_RET_ADDR		(1)     /* return address */

#define FRAME_HEAD_LEN		(3)     /* length of the frame head */
#define NONT_NR_OFF		(3)
#define SUCC_ADDR_OFF		(2)     /* success continuation address */
#define FAIL_ADDR_OFF		(1)     /* fail continuation address */
#define OLD_FP			(0)     /* pointer to parent frame */
#define NR_FORMALS_OFF		(-1)
#define NR_LOCALS_OFF		(-2)
#define NR_SONS_OFF		(-3)
#define PASS2_CODE		(-4)    /* second pass code pointer */
#define ISTATE			(-5)    /* input state at start of rule */
#define NR_SUCCS_OFF		(-6)    /* nr of successes */
#define PMPRODPTR		(-7)    /* pointer to current positive memo production */
#define FAIL_OFF		(-8)	/* points to FAIL instruction */
#define NR_SONS_DONE		(-9)    /* nr of sons which have been called */
#define PENORIG			(-10)   /* penalty level at start of rule */
#define VAR_OFFSET		(11)    /* offset for variable frame part */

#define NR_FORMALS		(fp[NR_FORMALS_OFF].arg)
#define NR_LOCALS		(fp[NR_LOCALS_OFF].arg)
#define VARIABLE(nr)		(fp[-(long)(VAR_OFFSET + (nr))])

#define NR_SONS			(fp[NR_SONS_OFF].arg)
#define NONT_NR			(fp[NONT_NR_OFF].arg)
#define SON_FP(nr)		(fp[-(long)(VAR_OFFSET + NR_FORMALS + NR_LOCALS + (nr))].data)

#define START_INPUT_STATE	(fp[ISTATE].input_state)

/*------------------------------------------------------------------------------
// Accessing the negative memo values.
// Macro:
//	NEG_MEMO(no)
// Description:
//	Convert memo with number to value of memo.
//----------------------------------------------------------------------------*/
#define NEG_MEMO(no)		(((fp[ISTATE].input_state)->neg_memos)[no])

#define negmemo_is_unknown(nr)		(NEG_MEMO(nr) == NEGMEMO_UNKNOWN)
#define negmemo_is_blocked(nr,pen)	((NEG_MEMO(nr) != NEGMEMO_UNKNOWN)&&(pen >= NEG_MEMO(nr)))
#define negmemo_set_blocked(nr,pen)	(NEG_MEMO(nr) = pen)
#define negmemo_set_succeeded(nr)	(NEG_MEMO(nr) = NEGMEMO_SUCCESS)

static unsigned long memo_enabled(long rule_nr)
{ return memo_enable_table[rule_nr];
}


/*
   The following routine is for debugging and statistical purposes:
*/
void print_negmemo_table (Trellis *trellis, int skip_unknown)
{   StateNode** state_row = GET_TRELLIS_STATE_ROW(trellis);
    int node_nr;
    int alt_nr;
    int* empty_rule;
    int* empty_node;

    long** overview = (long**) abs_calloc (trellis -> length, sizeof (long*),
					   "print_negmemo_table: overview[]");

    abs_message ("print_negmemo_table: there are %u negative memos.", get_nr_neg_memos());

    /* Build the table: */
    for (node_nr = 0; node_nr < trellis -> length; node_nr++)
      { StateNode* state = *state_row++;

	if(!state) overview[node_nr] = NULL;
	else
	  { NegMemo* neg_memo_vec = state -> neg_memos;

	    if (!neg_memo_vec) overview[node_nr] = NULL;
	    else
	      { overview[node_nr] = (long*) abs_calloc (get_nr_neg_memos(), sizeof(long),
							"print_negmemo_table: overview[][]");

	        for (alt_nr = 0; alt_nr < get_nr_neg_memos(); alt_nr++)
	           overview[node_nr][alt_nr] = neg_memo_vec[alt_nr];
	      };
	  };
      };

    /* printed table compression */
    empty_rule = (int*) abs_calloc (get_nr_neg_memos(), sizeof(int),
				    "print_negmemo_table: empty_rule");
    for (alt_nr = 0; alt_nr < get_nr_neg_memos (); alt_nr++)
      { empty_rule[alt_nr] = 1;
	node_nr = 0;

	while ((node_nr < trellis -> length) && (empty_rule[alt_nr]))
	  { if (overview[node_nr])
	      { if (overview[node_nr][alt_nr] == NEGMEMO_UNKNOWN)
		  empty_rule[alt_nr] = skip_unknown;
	        else empty_rule[alt_nr] = 0;
	      };
	    node_nr++;
	  };
      };
    empty_node = (int*) abs_calloc (trellis -> length, sizeof(int),
				    "print_negmemo_table: empty_node");
    for (node_nr = 0; node_nr < trellis->length; node_nr++)
      { empty_node[node_nr] = 1;
	alt_nr = 0;

	while ((alt_nr < get_nr_neg_memos()) && (empty_node[node_nr]) && (overview[node_nr]))
	  { switch (overview[node_nr][alt_nr])
	      { case NEGMEMO_UNKNOWN:	empty_node[node_nr] = skip_unknown; break;
	        case NEGMEMO_BLOCKED:	empty_node[node_nr] = 0; break;
	        default: empty_node[node_nr] = !overview[node_nr][alt_nr];
	      };
	    alt_nr++;
	  };
      };

    /* actually print it: */
    /* first the table */
    for (alt_nr = 0; alt_nr < get_nr_neg_memos(); alt_nr++)
      if (!empty_rule[alt_nr])
	{ abs_printf ("%3d|", alt_nr);
	  for (node_nr = 0; node_nr < trellis->length; node_nr++)
	    if (!empty_node[node_nr])
	      { switch (overview[node_nr][alt_nr])
		  { case NEGMEMO_BLOCKED: abs_printf("          b"); break;
	            case NEGMEMO_UNKNOWN: abs_printf("          u"); break;
	            default: abs_printf(" %10ld", overview[node_nr][alt_nr]);
	          };
	      };
	  abs_printf("\n");
	};

    /* then a neat line below it */
    abs_printf("---+");
    for (node_nr = 0; node_nr < trellis -> length; node_nr++)
      if (!empty_node[node_nr]) abs_printf ("-----------");

    /* and of course the numbers */
    abs_printf("\n   |");
    for (node_nr = 0; node_nr < trellis -> length; node_nr++)
      if (!empty_node[node_nr]) abs_printf (" %10d", node_nr);
    abs_printf ("\n");

    /* free the space: */
    for (node_nr = 0; node_nr < trellis -> length; node_nr++)
      if (overview[node_nr])
	abs_free (overview[node_nr], "print_negmemo_table: overview[][]");

    abs_free (overview, "print_negmemo_table: overview[]");
    abs_free (empty_rule, "print_negmemo_table: empty_rule");
    abs_free (empty_node, "print_negmemo_table: empty_node");
}

#define STATE_GET_ENDPOS(s) (s ? STATE_POS(s) : UINT_MAX)

#ifdef PMRTS
/*------------------------------------------------------------------------------
// Maintain a stack of ambiguous node during pass2 to keep track of the
// different printings of the parse forest.
//----------------------------------------------------------------------------*/
static PosMemo *ambi_stack;
static int ambi_size;
static int ambi_sptr;
static void init_ambi_stack ()
{ ambi_size = 64;
  ambi_sptr = 0;
  ambi_stack = abs_calloc (ambi_size, sizeof (PosMemo *), "init_ambi_stack");
}

static void push_ambi_stack (PosMemo prime)
{ if (ambi_sptr == ambi_size)
    { ambi_size *= 2;
      ambi_stack = abs_realloc (ambi_stack, ambi_size * sizeof (PosMemo *), "push_ambi_stack");
    };
  ambi_stack[ambi_sptr++] = prime;
  ambi_stack[ambi_sptr++] = prime;
}

static int try_lookup_sibling_in_ambi_stack (PosMemo pmptr, PosMemo *sibling)
{ PosMemo prime = pmptr -> prime;
  int ix;
  for (ix = 0; ix < ambi_sptr; ix += 2)
    if (ambi_stack[ix] == prime)
      { *sibling = ambi_stack [ix + 1];
	return (1);
      };
  return (0);
}

static int try_update_ambi_stack ()
{ while (ambi_sptr > 0)
    { PosMemo current_top = ambi_stack[ambi_sptr - 1];
      if (current_top -> equiv != NULL)
	{ /* We have another equivalent parse */
	  ambi_stack[ambi_sptr - 1] = current_top -> equiv;
	  return (1);
	};
      ambi_sptr -= 2;
    };
  return (0);
}

static void finish_ambi_stack ()
{ abs_free (ambi_stack, "finish_ambi_stack");
}
#endif /* PMRTS */

/*------------------------------------------------------------------------------
// Check to see if a pointer points in the agfl interpreter stack
//----------------------------------------------------------------------------*/
int pointsIntoStack (void *p) 
{
    return ((p >= (void *) stack) && (p < (void *) (stack + STACK_SIZE)));
}

/*------------------------------------------------------------------------------
// Instruction switching macros. 
//
// Macro:
//	NEXT
// Description:
//	Execute next instruction. Assume newpc and label are set correctly.
//
// Macro:
//	GOTO(addr)
// Description:
//	Go to instruction at address addr.
//----------------------------------------------------------------------------*/
#define NEXT			\
{				\
    pc = newpc;			\
    goto *label;		\
}

#define GOTO(x)			\
{				\
    CODE* _x = (x);		\
    label = _x -> action;	\
    pc = _x;			\
    goto *label;		\
}

/*------------------------------------------------------------------------------
// Macros to push and pop to and from the stack
// Note that the SP grows from high to low memory
// always pointing to the first free location
//----------------------------------------------------------------------------*/
#define PUSH_VALUE(x)	\
{	                \
    sp -> val = x;	\
    sp--;               \
}

#define PUSH_ADDR(x)	\
{	                \
    sp -> code = x;	\
    sp--;	        \
}

#define POP_VALUE(x)    \
{	                \
    sp++;	        \
    x = sp[0].val;	\
}

#define POP_ADDR(x)	\
{	                \
    sp++;               \
    x = sp[0].code;     \
}

/* Opcode counting */
#define opc_count(x) { if (count_option) opcode_count[0][x]++; }

#if defined(DEBUG_INTERPRETER)
#if !defined(PMRTS)
#define logexec(x) { \
    abs_message ("logexec: %s fp=%p sp=%p state=%p", \
	         translation[x].name, fp, sp, i_state);\
}

#define logfail(x) { \
    abs_message ("logfail: %s", translation[x].name); \
}

#else /* PMRTS */
#define logexec(x) { \
    abs_message ("logexec(%c): %s pc=%p fp=%p sp=%p state=%p", \
	         'A', translation[x].name, pc, fp, sp, i_state);\
}

#define logfail(x) { \
    abs_message ("logfail(%c): %s\n", \
	         'A', translation[x].name); \
}
#endif /* PMRTS */
#else /* NOT DEBUG_INTERPRETER */
#define logexec(x)
#define logfail(x)
#endif /* DEBUG_INTERPRETER */

/*------------------------------------------------------------------------------
// Function:
//	StateIndicator agfl_interpret (enum Runlevel runlevel, Trellis* trellis,
//		                       StateIndicator start_state)
//
// Description:
//	The AGFL interpreter. The interpreter can be called with
//	the following runlevels, selecting the appropriate functionality:
//	* Translate
//		Initialization mode replacing numbers of instructions with 
//		labels of instruction. Performed only once at initialization.
//	* Run
//		Parsing mode; execute grammar.
//	* Statistics
//		Display statistics of instructions (define STATISTICS).
//----------------------------------------------------------------------------*/
enum Runlevel { Translate, Run, Statistics };

static StateIndicator agfl_interpret (enum Runlevel runlevel, Trellis* const trellis,
	       			      StateIndicator start_state)
{

/*------------------------------------------------------------------------------
// Instruction opcodes.
//
// The interpreter implements the following instructions. The number of
// each instruction must correspond to the opcode number defined in the
// machdep file.
//
// The instruction size is the number of words occupied by the instruction
// sequence (opcode + parameters).
//----------------------------------------------------------------------------*/

    enum instruction {
	unknown = 0,
	create = 1,
	cont,
	fail,

	call,
	ucall,

	clear,
	init_i,
	init_s,
	init_t,

	adjust_i,
	adjust_s,
	adjust_t,
	adjust_v,

	uadjust_i,
	uadjust_s,
	uadjust_t,
	uadjust_v,

	restrict_i,
	restrict_s,
	restrict_t,
	restrict_v,

	urestrict_i,
	urestrict_s,
	urestrict_t,
	urestrict_v,

	match,
	skip_re,
	match_re,
	umatch,
	umatch_re,

	lex_match,
	lex_umatch,

	penalty,
	upenalty,

	commit,

	success,
	usuccess,
	end,
	pass2,

	retrn,
	push_f,
	push_l,
	print_son,

	print_abeg,
	print_aend,
	print_pbeg,
	print_psep,
	print_pend,

	print_val,
	print_term,
	print_lex,
	print_re,

	print_pen,

	print_n,
	print_il,
	print_sl,
	print_tl,
	print_vl,
	print_if,
	print_sf,
	print_tf,
	print_vf,

	tmemo,
	tsmemo,
	smemo_s,

	choice,
	uchoice,
	alt_marker,
	ualt_marker,

	root_call,
	root_create,

	mark_lrec,
	umark_lrec,

	trace,
	position,
	input_pos,
	uinput_pos,
	print_pos,

	match_other,
	umatch_other,
	print_other,

	/* Mark the last instruction and do not generate this one */
	nr_of_instructions,
    };

    /*
       opcode counting structures
       2 dimensions: 1 for stage, second for opcodes
       2nd +2 because of numbering of opcodes
    */
    static int opcode_count[2][nr_of_instructions];
    int opcode_index; /* to be used in loop counting */

    enum instruction_sz {
	create_sz = 5,
	cont_sz = 3,
	fail_sz = 1,

	call_sz = 4,
	ucall_sz = 2,

	clear_sz = 2,
	init_i_sz = 3,
	init_s_sz = 3,
	init_t_sz = 3,

	adjust_i_sz = 4,
	adjust_s_sz = 4,
	adjust_t_sz = 4,
	adjust_v_sz = 4,

	uadjust_i_sz = 3,
	uadjust_s_sz = 3,
	uadjust_t_sz = 3,
	uadjust_v_sz = 3,

	restrict_i_sz = 4,
	restrict_s_sz = 4,
	restrict_t_sz = 4,
	restrict_v_sz = 4,

	urestrict_i_sz = 2,
	urestrict_s_sz = 2,
	urestrict_t_sz = 2,
	urestrict_v_sz = 2,

	match_sz = 5,
	skip_re_sz = 5,
	match_re_sz = 5,
	umatch_sz = 2,
	umatch_re_sz = 2,

	lex_match_sz = 3,
	lex_umatch_sz = 2,

	penalty_sz = 3,
	upenalty_sz = 2,
	commit_sz = 2,

	success_sz = 2,
	usuccess_sz = 1,
	end_sz = 3,
	pass2_sz = 3,

	retrn_sz = 1,
	push_f_sz = 2,
	push_l_sz = 2,
	print_son_sz = 2,

	print_abeg_sz = 2,
	print_aend_sz = 1,
	print_pbeg_sz = 1,
	print_psep_sz = 1,
	print_pend_sz = 1,

	print_val_sz = 2,
	print_term_sz = 2,
	print_lex_sz = 2,
	print_re_sz = 2,

	print_pen_sz = 2,

	print_n_sz = 2,
	print_il_sz = 2,
	print_sl_sz = 3,
	print_tl_sz = 2,
	print_vl_sz = 1,
	print_if_sz = 2,
	print_sf_sz = 3,
	print_tf_sz = 2,
	print_vf_sz = 1,

	tmemo_sz = 3,
	tsmemo_sz = 3,
	smemo_s_sz = 2,

	choice_sz = 3,
	uchoice_sz = 2,
	alt_marker_sz = 2,
	ualt_marker_sz = 1,

	root_call_sz = 4,
	root_create_sz = 4,

	mark_lrec_sz = 1,
	umark_lrec_sz = 2,

	trace_sz = 2,
	position_sz = 2,

	input_pos_sz = 4,
	uinput_pos_sz = 3,
	print_pos_sz = 2,

	match_other_sz = 5,
	umatch_other_sz = 2,
	print_other_sz = 2,

	nr_of_instructions_sz = 1,		/* This last one is not to be used */
    };

/*------------------------------------------------------------------------------
// Opcode decoding table.
//
// The instruction translation table defines for each instruction its name,
// the label of its code and its length. If STATISTICS is defined,
// counters for execution, failure and looping are allocated for each
// instruction.
// 
// Note: The offset of each instruction in the table must correspond to
// the opcode number defined in the machdep file.
//----------------------------------------------------------------------------*/

    typedef struct {
	char	*name;
	void	*label;
	int	length;
    } opcode;

    static opcode translation[] =
    { 
	{ NULL, 0, 0 }, 

	{ "CREATE", &&CREATE, create_sz },
	{ "CONT", &&CONT, cont_sz },
	{ "FAIL", &&FAIL, fail_sz },
	
	{ "CALL", &&CALL, call_sz },
	{ "UCALL", &&UCALL, ucall_sz },

	{ "CLEAR", &&CLEAR, clear_sz },
	{ "INIT_I", &&INIT_I, init_i_sz },
	{ "INIT_S", &&INIT_S, init_s_sz },
	{ "INIT_T", &&INIT_T, init_t_sz },

	{ "ADJUST_I", &&ADJUST_I, adjust_i_sz },
	{ "ADJUST_S", &&ADJUST_S, adjust_s_sz },
	{ "ADJUST_T", &&ADJUST_T, adjust_t_sz },
	{ "ADJUST_V", &&ADJUST_V, adjust_v_sz },

	{ "UADJUST_I", &&UADJUST_I, uadjust_i_sz },
	{ "UADJUST_S", &&UADJUST_S, uadjust_s_sz },
	{ "UADJUST_T", &&UADJUST_T, uadjust_t_sz },
	{ "UADJUST_V", &&UADJUST_V, uadjust_v_sz },

	{ "RESTRICT_I", &&RESTRICT_I, restrict_i_sz },
	{ "RESTRICT_S", &&RESTRICT_S, restrict_s_sz },
	{ "RESTRICT_T", &&RESTRICT_T, restrict_t_sz },
	{ "RESTRICT_V", &&RESTRICT_V, restrict_v_sz },

	{ "URESTRICT_I", &&URESTRICT_I, urestrict_i_sz },
	{ "URESTRICT_S", &&URESTRICT_S, urestrict_s_sz },
	{ "URESTRICT_T", &&URESTRICT_T, urestrict_t_sz },
	{ "URESTRICT_V", &&URESTRICT_V, urestrict_v_sz },

	{ "MATCH", &&MATCH, match_sz },
	{ "SKIP_RE", &&SKIP_RE, skip_re_sz },
	{ "MATCH_RE", &&MATCH_RE, match_re_sz },
	{ "UMATCH", &&UMATCH, umatch_sz },
	{ "UMATCH_RE", &&UMATCH_RE, umatch_re_sz },

	{ "LEX_MATCH", &&LEX_MATCH, lex_match_sz },
	{ "LEX_UMATCH", &&LEX_UMATCH, lex_umatch_sz },

	{ "PENALTY", &&PENALTY, penalty_sz },
	{ "UPENALTY", &&UPENALTY, upenalty_sz },

	{ "COMMIT", &&COMMIT, commit_sz },

	{ "SUCCESS", &&SUCCESS, success_sz },
	{ "USUCCESS", &&USUCCESS, usuccess_sz },
	{ "END", &&END, end_sz },
	{ "PASS2", &&PASS2, pass2_sz },

	{ "RETRN", &&RETRN, retrn_sz },
	{ "PUSH_F", &&PUSH_F, push_f_sz },
	{ "PUSH_L", &&PUSH_L, push_l_sz },
	{ "PRINT_SON", &&PRINT_SON, print_son_sz },

	{ "PRINT_ABEG", &&PRINT_ABEG, print_abeg_sz },
	{ "PRINT_AEND", &&PRINT_AEND, print_aend_sz },
	{ "PRINT_PBEG", &&PRINT_PBEG, print_pbeg_sz },
	{ "PRINT_PSEP", &&PRINT_PSEP, print_psep_sz },
	{ "PRINT_PEND", &&PRINT_PEND, print_pend_sz },

	{ "PRINT_VAL", &&PRINT_VAL, print_val_sz },
	{ "PRINT_TERM", &&PRINT_TERM, print_term_sz },
	{ "PRINT_LEX", &&PRINT_LEX, print_lex_sz },
	{ "PRINT_RE", &&PRINT_RE, print_re_sz },

	{ "PRINT_PEN", &&PRINT_PEN, print_pen_sz },

	{ "PRINT_N", &&PRINT_N, print_n_sz },
	{ "PRINT_IL", &&PRINT_IL, print_il_sz },
	{ "PRINT_SL", &&PRINT_SL, print_sl_sz },
	{ "PRINT_TL", &&PRINT_TL, print_tl_sz },
	{ "PRINT_VL", &&PRINT_VL, print_vl_sz },
	{ "PRINT_IF", &&PRINT_IF, print_if_sz },
	{ "PRINT_SF", &&PRINT_SF, print_sf_sz },
	{ "PRINT_TF", &&PRINT_TF, print_tf_sz },
	{ "PRINT_VF", &&PRINT_VF, print_vf_sz },

	{ "TMEMO", &&TMEMO, tmemo_sz },
	{ "TSMEMO", &&TSMEMO, tsmemo_sz },
	{ "SMEMO_S", &&SMEMO_S, smemo_s_sz },

	{ "CHOICE", &&CHOICE, choice_sz },
	{ "UCHOICE", &&UCHOICE, uchoice_sz },
	{ "ALT_MARKER", &&ALT_MARKER, alt_marker_sz },
	{ "UALT_MARKER", &&UALT_MARKER, ualt_marker_sz },

	{ "ROOT_CALL", &&ROOT_CALL, root_call_sz },
	{ "ROOT_CREATE", &&ROOT_CREATE, root_create_sz },

	{ "MARK_LREC", &&MARK_LREC, mark_lrec_sz },
	{ "UMARK_LREC", &&UMARK_LREC, umark_lrec_sz },

	{ "TRACE", &&TRACE, trace_sz },
	{ "POSITION", &&POSITION, position_sz },
	{ "INPUT_POS", &&INPUT_POS, input_pos_sz },
	{ "UINPUT_POS", &&UINPUT_POS, uinput_pos_sz },
	{ "PRINT_POS", &&PRINT_POS, print_pos_sz },

	{ "MATCH_OTHER", &&MATCH_OTHER, match_other_sz },
	{ "UMATCH_OTHER", &&UMATCH_OTHER, umatch_other_sz },
	{ "PRINT_OTHER", &&PRINT_OTHER, print_other_sz },

	{ NULL, 0, 0}
    };

/*------------------------------------------------------------------------------
// Interpreter registers.
//
//	pc
//		program counter; contains address of current instruction.
//
//	newpc
//		new program counter; contains address of next instruction.
//
//	label
//		contains label of implementation code of next instruction.
//
//	sp
//		stack pointer; points to next free position on stack
//
//	fp
//		frame pointer; points to current stack frame.
//
//	pass2fp
//		Pass 2 frame pointer
//
//	i_state
//		state pointer pointing to current input state
//
//	i_transition
//		points to current transition to be matched
//		(only used during LEX_MATCH/LEX_UMATCH)
//
//	curr_penalty
//		the current penalty level
//
//	indent
//		Current indentation to print the parse tree
//----------------------------------------------------------------------------*/

    register CODE* pc = 0;
    register CODE* newpc = 0; 
    register void* label = 0;
    register DATA* fp = 0;
    DATA* pass2fp = 0;
#ifdef PMRTS
    PosMemo pmptr = NULL;
    int return_to_pass2 = 0;
#endif	/* PMRTS */
    register DATA* sp = 0;
    StateIndicator i_state = 0;
    ARG current_terminal = 0;

#ifdef GEN_RTS
    long chosen_freq = 0;
    long alt_depth = 0;

#ifdef DEBUG_GENERATOR
#define ALT_DEPTH_RESET (alt_depth = 0)
#define ALT_DEPTH_CHECK (assert(alt_depth >= 0))
#define ALT_DEPTH_DECREASE \
{	                                                                 \
    alt_depth--;	                                                  \
    abs_message ("ALT_DEPTH_DECREASE: alt_depth now %ld",alt_depth);    \
}
#define ALT_DEPTH_INCREASE \
{	                                                                   \
    alt_depth++;	                                                    \
    abs_message ("ALT_DEPTH_INCREASE: alt_depth now %ld",alt_depth);    \
}
#else /* DEBUG_GENERATOR */
#define ALT_DEPTH_RESET (alt_depth = 0)
#define ALT_DEPTH_CHECK (assert(alt_depth >= 0))
#define ALT_DEPTH_DECREASE (alt_depth--)
#define ALT_DEPTH_INCREASE (alt_depth++)
#endif /* DEBUG_GENERATOR */
#endif /* GEN_RTS */

    Transition *i_transition = NULL;
    Penalty curr_penalty = NO_PENALTY;
    int indent = 0;

/*------------------------------------------------------------------------------
// Initializing the interpreter.
//
// Description:
//	In the generated code, each instruction number is replaced with
//	the address of the label of the code implementing the instruction.
//
//	The call, fcall, save_f, restore_f, save_l, restore_l, penalty,
//	and upenalty instructions have specialized versions for common cases,
//	and generalized versions of other cases.
//
//	For the cont instruction, either the code for printing the
//	transduction or printing the parse tree is selected.
//
//	For the prof_exec and prof_succ instruction, the profile counter
//	is stored in the code. In the profile table, pointers to the
//	storage locations in the code are filled in.
//
//	If the programs mode is not very verbose, the arguments of print_pen,
//	and print_abeg are overwritten with 0, disabling the printing
//	of penalties and alternative numbers in the parse tree.
//----------------------------------------------------------------------------*/
    switch (runlevel) {
	case Translate: {
	    long instr;

	    pc = &root;

	    while ((instr = pc[0].arg) > 0) {
#ifdef DEBUG_TRANSLATE
	        abs_message ("translating %s", translation[instr].name);
#endif /* DEBUG_TRANSLATE */

	        switch (instr) {
	            case (long) cont:
	                if (!transduce_option) pc[1].arg = pc[2].arg;
	                pc[0].action = translation[cont].label;
	                pc += cont_sz;
	                break;

	            case (long) pass2:
	                if (!transduce_option) pc[1].arg = pc[2].arg;
	                pc[0].action = translation[pass2].label;
	                pc += pass2_sz;
	                break;

/*------------------------------------------------------------------------------
// The type and number of the terminal (skip, match, lexicon, other, grammar)
// have been encoded in state->terminal. The same encoding should be applied to
// the terminal numbers in the match and un-match instructions. Notice that the
// nonterminal number and arity of each LEX_MATCH and LEX_UMATCH have already
// been coded by the agfl compiler.
//----------------------------------------------------------------------------*/
#ifndef GEN_RTS
	            case match:
	                pc[0].action = translation[instr].label;
	                pc[1].arg = ENCODE_TERM(pc[1].arg);
	                pc += translation[instr].length;
	                break;

	            case match_re:
	                pc[0].action = translation[instr].label;
	                pc[1].arg = ENCODE_MATCH(pc[1].arg);
	                pc += translation[instr].length;
	                break;

	            case skip_re:
	                pc[0].action = translation[instr].label;
	                pc[1].arg = ENCODE_SKIP(pc[1].arg);
	                pc += translation[instr].length;
	                break;

		    case match_other:
			pc[0].action = translation[instr].label;
			pc[1].arg = ENCODE_OTHER(pc[1].arg);
			pc += translation[instr].length;
			break;
#endif

/*------------------------------------------------------------------------------
// Replace the number of each abstract instruction with the label of
// the code implementing the instruction, and advance the pc to the
// next instruction.
//----------------------------------------------------------------------------*/
	            default:
	                pc[0].action = translation[instr].label;
	                pc += translation[instr].length;
	                break;
	        }
	    }
	    return NULL;
	}

/*------------------------------------------------------------------------------
// Printing the instruction execution counters.
//----------------------------------------------------------------------------*/

	case Statistics: {
/*	  abs_bug ("agfl_interpret", "Statistics requested");*/
	    if (count_option) {
	        abs_message ("----- Start of opcode statistics -----");
	        for (opcode_index = 0; opcode_index <= trace + 1; opcode_index++) {
	            if (translation[opcode_index].name) {
	                abs_message ("%3d: %14s %10d %10d",
	                       opcode_index, translation[opcode_index].name,
	                       opcode_count[0][opcode_index], opcode_count[1][opcode_index]);
	            }
	            else {
	                abs_message ("%3d: %14s %10d %10d",
	                       opcode_index, "(no name)", opcode_count[0][opcode_index],
			       opcode_count[1][opcode_index]);
	            }
	        }
	        abs_message ("----- End of opcode statistics -----");
	    }
	    return NULL;
	}

/*------------------------------------------------------------------------------
// Interpret the compiled grammar.
//
// Description:
//	Set up the interpreters registers and jump to the first instruction.
//
// Note:
//	The interpreter should have been initialized and reset.
//----------------------------------------------------------------------------*/

	case Run: {
	/*----------------------------------
	// Initialize interpreter registers
	//--------------------------------*/
	    curr_penalty = NO_PENALTY;
	    stack = abs_calloc (STACK_SIZE, sizeof(cel), "agfl_interpret: stack");
	    fp = stack + STACK_SIZE - 1 - FRAME_HEAD_LEN;
	    sp = fp;
	    pc = &root;

	    if (count_option)
	      for (opcode_index = 0; opcode_index <= trace + 1; opcode_index++)
	        { opcode_count[0][opcode_index] = 0;
	          opcode_count[1][opcode_index] = 0;
	        };

#ifndef GEN_RTS
	    if (segment_mode && (start_state != NULL)) i_state = start_state;
	    else i_state = GET_FIRST_STATE_INDICATOR(trellis);
#else /* GEN_RTS */
	    i_state = NULL;
	    choice_admin_init ();
	    chosen_freq = 0;
	    ALT_DEPTH_RESET;
#endif /* GEN_RTS */

	    indent = 0;

	    /*
	    ** Start interpreter
	    */
	    start_parse_time();
	    goto *(pc[0].action);
	}
    }
    abs_bug ("agfl_interpret", "invalid runlevel");

/*------------------------------------------------------------------------------
// Implementation of abstract instructions.
//----------------------------------------------------------------------------*/
CONT:
    {
#ifdef PMRTS
	DB_RTS(abs_message ("CONT: curr_penalty = %d", curr_penalty));
	if (!memo_enabled (NONT_NR))
	  {
#endif
	    unsigned ix = NR_FORMALS;
	    CODE* routine = pc[1].code;

	    fp[NR_SUCCS_OFF].arg++;               /* for commit: parsing found */

	    logexec(cont);
	    opc_count(cont);

	    sp -= 2;
	    sp[2].code = pc + cont_sz;
	    sp[1].data = fp;

	    /* push result parameters */
	    while (ix > 0)
	      { ix--;
	        PUSH_VALUE(VARIABLE(ix).val);
	        DB_RTS(abs_message ("pushing value 0x%lx", VARIABLE(ix).val.set_par));
	      }

	    fp[PASS2_CODE].code = routine;
	    DB_RTS(abs_message ("2nd pass routine @%p", routine));

	    /* continue with the continuation... */
	    newpc = fp[SUCC_ADDR_OFF].code;
	    label = newpc -> action;

	    /* ...which assumes execution in the previous stack frame: */
	    fp = fp[OLD_FP].data;
	    DB_RTS(abs_message ("CONTinuing into \"%s\"", nonterm_names[NONT_NR]));

	    NEXT;
#ifdef PMRTS
	  }
	else /* The usual case for posmemo code */
	  { CODE* routine = pc[1].code;

	    logexec(cont);
	    opc_count(cont);

	    fp[NR_SUCCS_OFF].arg++;               /* for commit: parsing found */

	    posmemo_add_production (START_INPUT_STATE, NONT_NR,
				    curr_penalty - fp[PENORIG].penalty,
	                            NR_FORMALS, NR_LOCALS, fp[NR_SONS_DONE].arg,
				    &(VARIABLE(0).val), i_state, routine);

	    /* Step to next instruction */
	    newpc = pc + cont_sz;
	    label = newpc -> action;
	    NEXT;
	  };
#endif /* PMRTS */
    }

/*-------------------------------------------------------------------------
// Instruction: CREATE (nr formals, nr locals, nr sons, fail)
// Description: Setup a stack frame 
//	        Note that the frame header has already been setup by CALL
//-----------------------------------------------------------------------*/
CREATE:
    {	ARG nr_formals = pc[1].arg;
	ARG nr_locals = pc[2].arg;
	ARG nr_sons = pc[3].arg;
	CODE *fail_addr = pc[4].code;
	int total_alloc = nr_formals + nr_locals + nr_sons;

	logexec(create);
	opc_count(create);

	/* Link and allocate frame */
	sp[0].data = fp;                /* store old fp */
	fp = sp;
	sp -= total_alloc + VAR_OFFSET;

	/* Setup variable administration */
	NR_FORMALS = nr_formals;
	NR_LOCALS = nr_locals;
	NR_SONS = nr_sons;

	DB_RTS(abs_message ("CREATE(%ld, %ld, %ld, FAIL)", NR_FORMALS, NR_LOCALS, NR_SONS));

	/* Initialize remainder of frame */
	START_INPUT_STATE = i_state;
	fp[FAIL_OFF].code = fail_addr;
	fp[NR_SUCCS_OFF].arg = 0;
	fp[NR_SONS_DONE].arg = 0;
	fp[PENORIG].penalty = curr_penalty;

	DB_RTS(abs_message ("CREATE: start_input_state = %p, nont nr = %ld, penalty = %d",
			    i_state, NONT_NR, curr_penalty));

#ifdef PMRTS
        /*
	   Initialise the locals and sons to 0, since otherwise equal_posmemo()
           may look at uninitialised values sometimes (and fail to detect
           identity). This happens because not every alternative uses (sets) the
           full set of locals and sons so some may remain uninitialised.
        */
	if (memo_enabled (NONT_NR))
	  fp[PMPRODPTR].pmprod = posmemo_get_prod_ptr (i_state, NONT_NR);

	/*
	   If the positive memo is known for this input state and not blocked,
	   we continue through the fail continuation
	*/
	if (memo_enabled (NONT_NR))
	  { if (posmemo_is_known (i_state, NONT_NR) && posmemo_is_unblocked (i_state, NONT_NR))
	      { // CODE* failcont = posmemo_get_failcont (fp[PMPRODPTR].pmprod);
	        DB_RTS(abs_message ("CREATE: known & unblocked"));
	        fp[PMPRODPTR].pmprod = NULL;
	        GOTO (fail_addr);
	      }
	    else fp[PMPRODPTR].pmprod = NULL;
	  };
#endif /* PMRTS */

	/* Step to next instruction */
	newpc = pc + create_sz;
	label = newpc -> action;
	NEXT;
    }

#ifndef PMRTS
/*-------------------------------------------------------------------------
// Instruction: FAIL			(in case there is no positive memo)
// Description: restore the stack frame of the parent rule and continue
//	        with the fail continuation.
//-----------------------------------------------------------------------*/
FAIL:
    { logexec(fail);
      opc_count(fail);

      if (have_time_limit () && have_time_out ())
	/* leap to End */
	goto END;

      /* Pick up fail address from stack and restore sp and fp */
      newpc = fp[FAIL_ADDR_OFF].code; 
      label = newpc -> action;
      sp = fp + FRAME_HEAD_LEN;

      fp = fp[OLD_FP].data;
      fp[NR_SONS_DONE].arg--;
      NEXT;
    }

#else /* PMRTS */
/*-------------------------------------------------------------------------
// Instruction: FAIL			(positive memo)
// Description: current rule is done; fail if there are no successful
//	        productions, otherwise generate those productions
//	        by jumping to PM_GENERATE_PRODUCTION.
//-----------------------------------------------------------------------*/
FAIL:
    {	logexec (fail);
	opc_count (fail);

	if (have_time_limit () && have_time_out ())
	   /* leap to END */
	   goto END;

	i_state = START_INPUT_STATE;

	DB_RTS(abs_message ("FAIL in \"%s\"...", nonterm_names[NONT_NR]);
	       abs_message ("  into \"%s\"", nonterm_names[(fp[OLD_FP].data)[NONT_NR_OFF].arg]);
	       abs_message ("FAIL: start_input_state = %p", i_state));
	if (memo_enabled (NONT_NR))
	  { if (!posmemo_is_blocked (i_state, NONT_NR))
	      { PosMemo oldpmprod = fp[PMPRODPTR].pmprod;
	        DB_RTS(abs_message ("FAIL: \"%s\" is not blocked", nonterm_names[NONT_NR]));
	        if (oldpmprod)
		  { /* not the first time, restore penalty level... */
	            curr_penalty -= posmemo_get_penalty (oldpmprod);

		    /* ... and get next non equivalent production */
	            fp[PMPRODPTR].pmprod = posmemo_get_next_prod (oldpmprod);
	            if (fp[PMPRODPTR].pmprod != NULL)
		      { // posmemo_set_failcont(fp[PMPRODPTR].pmprod, pc);
	                DB_RTS(abs_message ("FAIL: productions left, generating..."));
	                goto PM_GENERATE_PRODUCTION;
		      }
	            else DB_RTS(abs_message ("FAIL: no more productions left, failing..."));
		  }
	        else
		  { /* first time we generate... */
	            fp[PMPRODPTR].pmprod = posmemo_get_prod_ptr (i_state, NONT_NR);

		    /* if this fails, the rule is marked unknown, which Should Not Happen (TM) */
	            assert(fp[PMPRODPTR].pmprod);
	            // posmemo_set_failcont(fp[PMPRODPTR].pmprod, pc);
	            goto PM_GENERATE_PRODUCTION;
	          };
	      }
	    else DB_RTS(abs_message ("FAIL: \"%s\" is blocked", nonterm_names[NONT_NR]));
	  };

	/* should not be needed, but just to be sure: */
	curr_penalty = fp[PENORIG].penalty;

	/* Pick up fail address from stack and restore sp and fp */
	newpc = fp[FAIL_ADDR_OFF].code; 
	label = newpc -> action;
	sp = fp + FRAME_HEAD_LEN;

	fp = fp[OLD_FP].data;
	fp[NR_SONS_DONE].arg--;
	NEXT;
    }

/*-------------------------------------------------------------------------
// Generate (more) productions
// Description: push formals to stack, set input state and penalty level,
// 	        and continue with success continuation.
//-----------------------------------------------------------------------*/
PM_GENERATE_PRODUCTION:
    { unsigned nr_formals = NR_FORMALS;
      void *memod_formals = posmemo_get_formal_ptr (fp[PMPRODPTR].pmprod);

      /* Save old PC and FP */
      sp -= 2;
      sp[2].code = pc;
      sp[1].data = fp; 

      /* push result parameters: check for 64 bit longs */
      sp -= nr_formals;
      memcpy (sp + 1, memod_formals, nr_formals * sizeof(Value));

      /* It may possible to fail for a penalty increase */
      i_state = posmemo_get_input_state (fp[PMPRODPTR].pmprod);
      curr_penalty += posmemo_get_penalty (fp[PMPRODPTR].pmprod);

      /* continue with the continuation... */
      newpc = fp[SUCC_ADDR_OFF].code;
      label = newpc -> action;

      /* ...which assumes execution in the previous stack frame: */
      fp = fp[OLD_FP].data;

      NEXT;
    }
#endif /* PMRTS */

/*---------------------------------------------------------------------------
// Instruction: CALL (syntax rule number, start point, fail continuation)
// Description: call to a rule by building the pre-call stack part and jump
//	        to the starting point. With positive memoization extra checks
//		are done: check if we already know the productions and if so,
//		jump to the appropriate FAIL instruction to produce the
//		results; if blocked (no productions) just fail;
//		if unknown enter the rule.
//-------------------------------------------------------------------------*/
CALL:
    { 	/* Pick up arguments */
	ARG rule_nr = pc[1].arg;
	CODE *jmp_point = pc[2].code;
	CODE *fail_addr = pc[3].code;
	CODE *succ_addr = pc + call_sz;

	logexec(call);
	opc_count(call);

	DB_RTS(abs_message ("CALLing \"%s\" from \"%s\"",
			    nonterm_names[rule_nr], nonterm_names[NONT_NR]));
	if (have_time_limit() && have_time_out())
	  /* take leap to END */
	  goto END;

#ifdef PMRTS
	/* Check if this rule is blocked for this position for left recursion */
	if (i_state -> lrec_markers[rule_nr])
	  { DB_RTS(abs_message ("CALL: \"%s\" is blocked for left recursion",
				nonterm_names[rule_nr]));
	    GOTO(fail_addr);
	  }

	/* Check the positive memo for this position */
	else if (memo_enabled (rule_nr))
	  { if (posmemo_is_blocked (i_state, rule_nr))
	      { DB_RTS(abs_message ("CALL: \"%s\" is blocked", nonterm_names[rule_nr]));
	        GOTO(fail_addr);
	      }
	    else if (posmemo_is_known (i_state, rule_nr))
	      DB_RTS(abs_message ("CALL: \"%s\" is known", nonterm_names[rule_nr]));
	    else
	      { /* positive memo is unknown */
	        posmemo_set_blocked (i_state, rule_nr);
	        DB_RTS(abs_message ("CALL: \"%s\" is unknown, changing to blocked",
	                            nonterm_names[rule_nr]));
	      };
	  };
#endif /* PMRTS */

	sp -= FRAME_HEAD_LEN;
	sp[FAIL_ADDR_OFF].code = fail_addr;
	sp[SUCC_ADDR_OFF].code = succ_addr;
	sp[NONT_NR_OFF].arg = rule_nr;

	assert (fp[NR_SONS_DONE].arg >= 0);
	assert (fp[NR_SONS_DONE].arg < NR_SONS);

	DB_RTS(abs_message ("sons done: %ld, son_fp = %p",
			    fp[NR_SONS_DONE].arg, fp + fp[NR_SONS_DONE].arg));
	SON_FP(fp[NR_SONS_DONE].arg) = sp;
	fp[NR_SONS_DONE].arg++;

	/* Jump to called address */
	newpc = jmp_point;
	label = newpc -> action;
	NEXT;
    }
 
/*---------------------------------------------------------------------------
// Instruction: UCALL(nr formals)
// Description: clean up previous result parameters from the stack and 
//	        jump to the continuation of the child rule.
//-------------------------------------------------------------------------*/
UCALL:
    { ARG nr_formals = pc[1].arg;
      logexec (ucall);
      opc_count (ucall);

      sp += nr_formals;
      newpc = sp[2].code;
      fp = sp[1].data;
      sp += 2;

      DB_RTS (abs_message ("UCALL(%ld) into rule %ld", nr_formals, NONT_NR));
      label = newpc -> action;
      NEXT;
    }

/*------------------------------------------------------------------------------
// Instructions on variables:
// Note: Instructions that implement predicates are also added here
//----------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------
// Instruction: CLEAR (nr)
// Description: Clear the frame at position 0..nr-1 to initialize
//		all formals and locals to 0.
//----------------------------------------------------------------------------*/
CLEAR:
    { ARG nr = pc[1].arg;
      ARG idx;
      for (idx = 0; idx < nr; idx++)
	 VARIABLE(idx).val = (Value) 0;

      /* Step to next instruction */
      newpc = pc + clear_sz;
      label = newpc -> action;
      NEXT;
    }

/*------------------------------------------------------------------------------
// Instruction: INIT_{IST}(position, value)
// Description: initialize INT/SET/TEXT variable at position (the position
//	        is relative to the frame pointer plus fixed part offset)
//	        to the specified value.
//----------------------------------------------------------------------------*/
INIT_I:
INIT_S:
INIT_T:
    { ARG pos = pc[1].arg;
      Value val = pc[2].val;

      logexec (init_s);				/* for proper accounting, this is not correct */
      opc_count (init_s);

      /* Initialize this variable */
      VARIABLE(pos).val = val;

      /* assumption: init_i_sz == init_s_sz == init_t_sz */
      assert (init_i_sz == init_s_sz);
      assert (init_i_sz == init_t_sz);

      /* Step to next instruction */
      newpc = pc + init_i_sz;
      label = newpc -> action;
      NEXT;
    }

/*------------------------------------------------------------------------------
// Instruction: ADJUST_{IST}(target, result param, fail)
// Description: restrict INT/SET/TEXT variable at target (the position
//	        is relative to the frame pointer plus fixed part offset)
//	        to the value after continuing the recent CALL
//
// Note: we use the location of the restriction result value to save the
//       original value. UADJUST will restore from this location
//----------------------------------------------------------------------------*/
ADJUST_I:
    { /* Pick up arguments */
      ARG target = pc[1].arg;
      ARG param = pc[2].arg;
      CODE* fail_addr = pc[3].code;

      /* Pick up restricting and original value */
      Value val = sp[param].val;
      Value orig = VARIABLE(target).val;
      logexec (adjust_i);			/* for proper accounting, this is not correct */
      opc_count (adjust_i);

      /* Do flat trellis operation */
      DB_RTS(abs_message ("ADJUST_I: adjusting to %d", val.int_par));
      if (val.int_par == TOP_INT)		/* No restriction but save original for restore */
	sp[param].val = orig;
      else if (orig.int_par == TOP_INT)		/* orig was unknown, assign new and save old */
	{ sp[param].val = orig;
	  VARIABLE(target).val = val;
	}
      else if (orig.int_par != val.int_par)	/* consistent substitution fails */
	GOTO(fail_addr);

      /* Step to next instruction */
      newpc = pc + adjust_i_sz;
      label = newpc -> action;
      NEXT;
    }

ADJUST_S:
    { /* Pick up arguments */
      ARG target = pc[1].arg;
      ARG param = pc[2].arg;
      CODE* fail_addr = pc[3].code;

      /* Pick up restricting and original value */
      Value val = sp[param].val;
      Value orig = VARIABLE(target).val;
      Value result;
      logexec (adjust_s);
      opc_count (adjust_s);

      /* Do the trellis operation and intersect the sets */
      result.set_par = orig.set_par & val.set_par;
      DB_RTS(abs_message ("ADJUST_S: %ld <- (orig) %ld & (res) %ld",
		 	  result.set_par, orig.set_par, val.set_par));

      if (result.set_par == 0)			/* intersection of the sets is empty -- fail */
	GOTO (fail_addr);

      /* save original value and set the variable to the intersection */
      sp[param].val = orig;
      VARIABLE(target).val = result;

      /* Step to next instruction */
      newpc = pc + adjust_s_sz;
      label = newpc -> action;
      NEXT;
    }

ADJUST_T:
    { /* Pick up arguments */
      ARG target = pc[1].arg;
      ARG param = pc[2].arg;
      CODE* fail_addr = pc[3].code;

      /* Pick up restricting and original value */
      Value val = sp[param].val;
      Value orig = VARIABLE(target).val;
      logexec (adjust_t);
      opc_count (adjust_t);

      /* Do flat trellis operation */
      DB_RTS(abs_message ("ADJUST_T: adjusting to %s", val.text_par));
      if (val.text_par == TOP_TEXT)		/* No restriction but save original for restore */
	sp[param].val = orig;
      else if (orig.text_par == TOP_TEXT)	/* orig was unknown, assign new and save old */
	{ sp[param].val = orig;
	  VARIABLE(target).val = val;
	}
      else if (strcmp (orig.text_par, val.text_par) != 0)
	{ GOTO(fail_addr);			/* consistent substitution fails */
	}
      else sp[param].val = orig;		/* Save orig pointer for restore */

      /* Step to next instruction */
      newpc = pc + restrict_t_sz;
      label = newpc -> action;
      NEXT;
    }

ADJUST_V:
    { /* Unimplemented */
      goto ILLEGAL;
    }

UADJUST_I:
UADJUST_S:
UADJUST_T:
    { ARG target = pc[1].arg;
      ARG param = pc[2].arg;
      Value val = sp[param].val;
	
      logexec(uadjust_s);
      opc_count(uadjust_s);

      VARIABLE(target).val = val;

      /* Step to next instruction */
      newpc = pc + uadjust_s_sz;
      label = newpc -> action;
      NEXT;
    }

UADJUST_V:
    { /* Unimplemented */
      goto ILLEGAL;
    }

/*------------------------------------------------------------------------------
// Instruction: RESTRICT_{IST}(target, value, fail)
// Description: restrict INT/SET/TEXT variable at target (the position
//	        is relative to the frame pointer plus fixed part offset)
//	        by the value. Upon success, the original value is pushed
//		for later restore by URESTRICT_{IST}
//----------------------------------------------------------------------------*/
RESTRICT_I:
    { /* Pick up arguments */
      ARG target = pc[1].arg;
      Value val = pc[2].val;
      CODE* fail_addr = pc[3].code;

      /* Pick up original value */
      Value orig = VARIABLE(target).val;
      logexec (restrict_i);
      opc_count (restrict_i);

      /* Do flat trellis operation */
      if (val.int_par == TOP_INT)		/* No restriction but push original for restore */
	{ PUSH_VALUE (orig);
	}
      else if (orig.int_par == TOP_INT)		/* orig was unknown, assign new and push old */
	{ PUSH_VALUE (orig);
	  VARIABLE(target).val = val;
	}
      else if (orig.int_par != val.int_par)	/* consistent substitution fails */
        { GOTO (fail_addr);
	}
      else PUSH_VALUE (orig);			/* Save for restore */

      /* Step to next instruction */
      newpc = pc + adjust_i_sz;
      label = newpc -> action;
      NEXT;
    }

RESTRICT_S:
    { /* Pick up arguments */
      ARG target = pc[1].arg;
      Value val = pc[2].val;
      CODE* fail_addr = pc[3].code;

      /* Pick up original value */
      Value orig = VARIABLE(target).val;
      Value result;
      logexec (restrict_s);
      opc_count (restrict_s);

      /* Do the trellis operation and intersect the sets */
      result.set_par = orig.set_par & val.set_par;
      if (result.set_par == 0)			/* intersection of the sets is empty -- fail */
	GOTO(fail_addr);

      /* push original value and assign intersection to variable */
      PUSH_VALUE (orig);
      VARIABLE(target).val = result;

      /* Step to next instruction */
      newpc = pc + restrict_s_sz;
      label = newpc -> action;
      NEXT;
    }

RESTRICT_T:
    { /* Pick up arguments */
      ARG target = pc[1].arg;
      Value val = pc[2].val;
      CODE* fail_addr = pc[3].code;

      /* Pick up original value */
      Value orig = VARIABLE(target).val;
      logexec(restrict_t);
      opc_count(restrict_t);

      /* Do flat trellis operation */
      if (val.text_par == TOP_TEXT)		/* No restriction but push original for restore */
        { PUSH_VALUE(orig);
	}
      else if (orig.text_par == TOP_TEXT)	/* orig was unknown, assign new and push old */
	{ PUSH_VALUE(orig);
	  VARIABLE(target).val = val;
	}
      else if (strcmp (orig.text_par, val.text_par) != 0)
	{ GOTO (fail_addr);			/* consistent substitution fails */
	}
      else PUSH_VALUE (orig);			/* Push orig pointer for restore */

      /* Step to next instruction */
      newpc = pc + restrict_t_sz;
      label = newpc -> action;
      NEXT;
    }

RESTRICT_V:
    { /* Unimplemented */
      goto ILLEGAL;
    }

URESTRICT_I:
URESTRICT_S:
URESTRICT_T:
    { ARG target = pc[1].arg;
	
      logexec (urestrict_s);			/* MS: Wrong accounting */
      opc_count (urestrict_s);

      POP_VALUE (VARIABLE(target).val);

      /* Step to next instruction */
      newpc = pc + urestrict_s_sz;
      label = newpc -> action;
      NEXT;
    }

URESTRICT_V:
    { /* Unimplemented */
      goto ILLEGAL;
    }

/*------------------------------------------------------------------------------
// Instruction: INPUT_POS (position, target, fail)
// Description: Allocate and sprintf "Fname, line %d, column %d" where the
//		values are taken from the current input trellis state
//		and save this string at position in the current frame
//		(relative to the frame pointer plus fixed part offset)
//		Without posmemo, UINPUT_POS frees this string.
//		With posmemo, the string is freed later
//
//		If a affix target is given (i.e.) the position is >= 0,
//		restrict this target with the sprinted value.
//		Upon success, the original value is pushed for later restore
//----------------------------------------------------------------------------*/
INPUT_POS:
    { /* Pick up arguments */
      ARG position = pc[1].arg;
      ARG target = pc[2].arg;
      CODE* fail_addr = pc[3].code;
      char *fname;
      char *dstr;

      /* Pick up original value */
      DB_RTS(abs_message ("INPUT_POS (%ld, %ld, failaddr)", position, target));
      logexec(input_pos);
      opc_count(input_pos);

      /* Calculate value and store in current frame */
      if (i_state == NULL)
	abs_abort ("INPUT_POS", "Cannot take position from null input state");
      fname = (char *) get_input_pos_fname ();	/* Do not nag about const */
      if (fname == NULL)
	dstr = abs_mm_new_fmtd_string ("$INPUT_POS", "line %d, column %d",
				       i_state -> linenr, i_state -> colnr);
      else dstr = abs_mm_new_fmtd_string ("$INPUT_POS", "File '%s', line %d, column %d",
					  fname, i_state -> linenr, i_state -> colnr);
      VARIABLE(position).str = dstr;

      /* If we have a target affix, do the corresponding trellis operation */
      if (target >= 0)
	{ /* Do flat trellis operation */
	  Value orig = VARIABLE(target).val;
          if (orig.text_par == TOP_TEXT)	/* orig was unknown, assign new and push old */
	    { PUSH_VALUE(orig);
	      VARIABLE(target).str = dstr;
	    }
          else if (strcmp (orig.text_par, dstr) != 0)
	    { GOTO (fail_addr);			/* consistent substitution fails */
	    }
          else PUSH_VALUE (orig);		/* Push orig pointer for restore */
        };

      /* Step to next instruction */
      newpc = pc + input_pos_sz;
      label = newpc -> action;
      NEXT;
    }

UINPUT_POS:
    { /* Pick up arguments */
      ARG target = pc[2].arg;
      logexec (uinput_pos);
      opc_count (uinput_pos);

      /* Restore the original target affix, if present */
      DB_RTS(abs_message ("UINPUT_POS (%ld, %ld)", pc[1].arg, target));
      if (target >= 0)
        POP_VALUE (VARIABLE(target).val);

      /* Step to next instruction */
      newpc = pc + uinput_pos_sz;
      label = newpc -> action;
      NEXT;
    }

/*--------------------------------------------------------------------------
// Optimization instructions
//------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------
// Instruction: TMEMO(nr, fail addr)
// Description: Test memo nr, if blocked for current or lower penalty level
//	      then goto fail addr, otherwise continue.
// Note: TMEMO is only concerned with negative memo
//       nr is the alt nr in the grammar
//------------------------------------------------------------------------*/
TMEMO:
    { /* Pick up arguments */
      ARG memo_nr = pc[1].arg;
      CODE* fail_addr = pc[2].code;
      logexec (tmemo);
      opc_count (tmemo);

      if (negmemo_is_blocked (memo_nr, curr_penalty))
	{ /* blocked for this penalty level -> fail */
	  logfail(tmemo);
	  newpc = fail_addr;
	  label = newpc -> action;
	  NEXT;
	};

      /* Step to next instruction */
      newpc = pc + tmemo_sz;
      label = newpc -> action;
      NEXT;
    }

/*--------------------------------------------------------------------------
// Instruction: TSMEMO(memo nr, fail addr)
// Description: Test and set memo: same as TMEMO, but if unknown set memo
//	      nr to blocked and continue.
// Note: TSMEMO is only concerned with negative memo
//       memo nr is the alt nr in the grammar
//------------------------------------------------------------------------*/
TSMEMO:
    {	/* Pick up arguments and log instructions */
	ARG memo_nr = pc[1].arg;
	logexec (tsmemo);
	opc_count (tsmemo);
	DB_RTS(abs_message ("TSMEMO(%lu) at pos %u", memo_nr, STATE_POS(i_state)));

	/*
	   If we know that we are blocked for this alternative at
	   this position, we immediately fail.
	*/
	if (negmemo_is_blocked (memo_nr, curr_penalty))
	  { /* negmemo is blocked -> fail */
	    DB_RTS(abs_message ("Current negmemo is %d against penalty %d, failing",
				NEG_MEMO (memo_nr), curr_penalty));
	    logfail (tsmemo);
	    newpc = pc[2].code;
	    label = newpc -> action;
	    NEXT;
	  };

	/*
	   If we are marked for left recursion, we know we are going to pass
           this point again, so do not set the negative memo to blocked
	*/
#ifdef PMRTS
	if (!(START_INPUT_STATE -> lrec_markers[NONT_NR]) && negmemo_is_unknown (memo_nr))
#else
	if (negmemo_is_unknown (memo_nr))
#endif
	  negmemo_set_blocked (memo_nr, curr_penalty);

	/* Step to next instruction */
	newpc = pc + tsmemo_sz;
	label = newpc -> action;
	NEXT;
    }

/*---------------------------------------------------------------------------
// Instruction: SMEMO_S(memo nr)
// Description: Set memo nr to succeeded for current penalty level.
// Note: SMEMO_S is only concerned with negative memo
//       memo nr is the alt nr in the grammar
//-------------------------------------------------------------------------*/
SMEMO_S:
    {	ARG memo_nr = pc[1].arg;

	logexec (smemo_s);
	opc_count (smemo_s);
	DB_RTS(abs_message ("SMEMO_S(%lu) at pos %u", memo_nr, STATE_POS(i_state)));

	/*
	   If we are not marked for left recursion and arrive here
	   while the negmemo is still unknown, something is wrong
        */
#ifdef PMRTS
	if (!(START_INPUT_STATE -> lrec_markers[NONT_NR]) && negmemo_is_unknown (memo_nr))
#else
	if (negmemo_is_unknown (memo_nr))
#endif
	  abs_abort ("SMEMO_S", "Negative memo is unknown when it should not be");
	  
	negmemo_set_succeeded (memo_nr);

        /* Step to next instruction */
	newpc = pc + smemo_s_sz;
	label = newpc -> action;
	NEXT;
    }

/*------------------------------------------------------------------------------
// Input matching instructions:
// Instruction: MATCH    (id, local, target, fail address)
//		MATCH_RE (id, local, target, fail address)
// 		SKIP_RE  (id, local, target, fail address)
//
// Matching of grammar terminals, regexps matches and skips
// is performed by the same code. In the initialization phase
// of the interpreter, the terminal type has been encoded in
// the first argument of the instruction.
//
// The local is used to hold the pointer to the actual transition in the
// trellis matching the terminal type and id. The target is the position
// in the current frame of a text affix whose value is adjusted by the
// matched text.
//----------------------------------------------------------------------------*/
MATCH:
MATCH_RE:
SKIP_RE:
MATCH_OTHER:
    {   Terminal terminal = pc[1].arg;		/* get token to be matched */
	logexec (match);
	opc_count (match);

#ifndef GEN_RTS
	i_transition = GET_STATE_TRANSLIST (trellis, i_state, DECODE_TERM_OR_RE_CLASS(terminal));

#ifdef TRACE_MATCH
	if (TERM_IS_MATCH (terminal))
	  abs_message ("MATCH MATCH_RE pos %u: %ld == \"%s\"",
	                STATE_POS(i_state), DECODE_REGEXP_NUMBER(terminal),
	                match_regexp_names[DECODE_REGEXP_NUMBER(terminal)]);
	else if (TERM_IS_SKIP (terminal))
	  abs_message ("MATCH SKIP_RE pos %u: %ld == \"%s\"",
	                STATE_POS(i_state), DECODE_REGEXP_NUMBER(terminal),
	                skip_regexp_names[DECODE_REGEXP_NUMBER(terminal)]);
	else if (TERM_IS_OTHER (terminal))
	  abs_message ("MATCH OTHER pos %u", STATE_POS(i_state));
	else /* TERM_IS_TERM */
	  abs_message ("MATCH pos%u: %ld == \"%s\"",
	                STATE_POS(i_state), DECODE_TERM_NUMBER(terminal),
	                term_names[DECODE_TERM_NUMBER(terminal)]);
#endif /* TRACE_MATCH */
	while (i_transition != NULL)
	  { /* do we have another entry? */
#ifdef TRACE_MATCH
	    abs_message ("state_has_eos_transition == %d", state_has_eos_transition(i_state));
	    abs_message ("c->t == %lu, t == %lu", i_transition -> terminal, terminal);
#endif /* TRACE_MATCH */
	    if (i_transition -> terminal != terminal)
	      /* This entry doesn't match, try next one */
	      i_transition = i_transition -> next;
	    else break;
	  };

MATCH_AGAIN:
	/*
	   If we got here through the goto from UMATCH[_RE, _OTHER], or fell
           through from above while loop, i_transition is always valid or NULL
	*/
	if (i_transition != NULL)
          { ARG position = pc[2].arg;		/* Position to save transition */
            ARG target = pc[3].arg;		/* Position of text affix */
	    char *dstr = i_transition -> text;	/* Matched text in trellis */

	    /* Push our current program location, transition and input position */
	    /* save input position on stack */
	    sp -= 3;
	    sp[3].code = pc;
	    sp[2].input_transition = i_transition;
	    sp[1].input_state = i_state;

#ifdef TRACE_MATCH
	    abs_message ("saved state = %p", i_state);
	    abs_message ("at local %ld = %p", position, &VARIABLE(position));
#endif /* TRACE_MATCH */

	    /* save trellis transition in local stack frame */
	    VARIABLE(position).input_transition = i_transition;
	    i_state = TRANSITION_DEST_STATE_INDICATOR (i_transition, trellis);
#ifdef TRACE_MATCH
	    abs_message ("new state = %p pos=%u", i_state, i_state ? STATE_POS(i_state) : -1);
#endif /* TRACE_MATCH */

	    /* adjust optional text affix; if absent, offset = -1 */
	    if (target >= 0)
	      { /* Do flat trellis operation */
		Value orig = VARIABLE(target).val;
		if (orig.text_par == TOP_TEXT)	/* orig was unknown, assign new and push old */
		  { PUSH_VALUE(orig);
		    VARIABLE(target).str = dstr;
		  }
		else if (strcmp (orig.text_par, dstr) != 0)
		  { /* consistent substitution fails */
		    PUSH_VALUE (orig);
		    goto UMATCH;
		  }
	        else PUSH_VALUE (orig);		/* Push orig pointer for restore */
              };

            /* Step to next instruction */
	    newpc = pc + match_sz;
	    label = newpc -> action;
	    NEXT;
	  };
#ifdef TRACE_MATCH
	abs_message ("MATCH[_RE,_OTHER]: no more transitions");
#endif /* TRACE_MATCH */

#else /* GEN_RTS */
	gen_output_add_token (term_names[terminal]);

        /* Step to next instruction */
	newpc = pc + match_sz;
	label = newpc -> action;
	NEXT;
#endif /* GEN_RTS */

	/* If all transitions are tried, nothing is left, so fail */
	GOTO(pc[4].code);
    }

UMATCH:
UMATCH_RE:
UMATCH_OTHER:
    {	/* note: unlike with LEX_UMATCH, the Terminal is not in pc[1] */
	ARG target = pc[1].arg;
	logexec (umatch);
	opc_count (umatch);

	/* Absent target is coded with -1 */
        if (target >= 0)
          POP_VALUE (VARIABLE(target).val);

#ifndef GEN_RTS
	i_state = sp[1].input_state;	/* restore previous input position */
	sp += 1;
#ifdef TRACE_MATCH
	abs_message ("restored state = %p", i_state);
#endif /* TRACE_MATCH */

	/* Restore the transition */
        i_transition = sp[1].input_transition;
        sp += 2;
        { Terminal terminal = i_transition -> terminal;

	  /* this transition already done */
	  i_transition = i_transition -> next;
	      
	  while ((i_transition != NULL) && (i_transition -> terminal != terminal))
	    /* This entry doesn't match, try next one */
	    i_transition = i_transition -> next;

	  if (i_transition != NULL)
	    { pc = sp[0].code;
	      goto MATCH_AGAIN;
	    };
        }

#else /* GEN_RTS */
	gen_output_remove_token();
#endif /* GEN_RTS */

	/* Step to next instruction */
	newpc = pc + umatch_sz;
	label = newpc -> action;
	NEXT; 
    }

#ifndef GEN_RTS
LEX_MATCH:
    {	Penalty pen_gain;

	current_terminal = pc[1].arg;   /* get lexicon nont to be matched */
	logexec(lex_match);
	opc_count(lex_match);

	sp -= 2;
	sp[1].input_state = i_state;        /* save current input position */
	sp[2].code = pc;                    /* save current program counter */

	i_transition = GET_STATE_TRANSLIST (trellis,i_state, DECODE_NONT_CLASS(current_terminal));

LEX_MATCH_AGAIN:

	if (i_transition == NULL) pen_gain = MAX_PENALTY;
        else if (hybrid_parsing_option) pen_gain = TRANSITION_PENALTY(i_transition);
	else pen_gain = 0;

	if (penalty_change_would_block (curr_penalty, pen_gain))
	  { i_state = sp[1].input_state;	/* restore input position */
	    sp += 2;
	    GOTO(pc[2].code);			/* and fail */
	  }
	else
	  { /* we can go on */
	    int i, arity;
	    TaggedValue const *val_ptr;
	    assert(i_transition->terminal == current_terminal);
	    curr_penalty += pen_gain;

	    /* restrict formals to lexicon entry parameters */
	    val_ptr = TRANSITION_PARAMS(i_transition);
	    arity = DECODE_NONT_ARITY(current_terminal);
	    for (i = arity - 1; i >= 0; --i)
	      { DB_RTS(abs_message ("pushing value 0x%lx", val_ptr[i].value.set_par));
	        PUSH_VALUE(val_ptr[i].value);
	      };

	    SON_FP(fp[NR_SONS_DONE].arg) = (DATA*) i_transition;
	    fp[NR_SONS_DONE].arg++;

	    i_state = TRANSITION_DEST_STATE_INDICATOR(i_transition,trellis);

	    newpc = pc + lex_match_sz;
	    label = newpc->action;
	    NEXT;
	  };
    }

LEX_UMATCH:
    {
	int arity;

	current_terminal = pc[1].arg;	    	/* get lexicon nont to be matched */

	logexec(lex_umatch);
	opc_count(lex_umatch);

	fp[NR_SONS_DONE].arg--;
	i_transition = (Transition *)SON_FP(fp[NR_SONS_DONE].arg);

	arity = DECODE_NONT_ARITY(current_terminal);
	if (hybrid_parsing_option)
	  { /* restore penalty level */
	    curr_penalty -= TRANSITION_PENALTY(i_transition);
	  };

	sp += arity;				/* remove previous results from the stack */
	pc = sp[2].code;			/* restore old pc */
	i_transition = i_transition -> next;
	goto LEX_MATCH_AGAIN;			/* try to match next entry */
    }

#else /* GEN_RTS */

LEX_MATCH:
    {	logexec (lex_match);
	opc_count (lex_match);
	goto ILLEGAL;
    }

LEX_UMATCH:
    {	logexec (lex_match);
	opc_count (lex_match);
	goto ILLEGAL;
    }
#endif /* GEN_RTS */


/*------------------------------------------------------------------------------
// Additional flow control instructions (penalty/commit):
//----------------------------------------------------------------------------*/
PENALTY:
    {	/* Pick up argument and fail address */
	Penalty penalty_gain = pc[1].penalty;
	CODE* fail_addr = pc[2].code;
	logexec(penalty);
	opc_count(penalty);

	DB_RTS(abs_message ("PENALTY(%d): old penalty %d", penalty_gain, curr_penalty));

	/* Check if new penalty allows continuation */
	if (penalty_change_would_block (curr_penalty, penalty_gain))
	  GOTO(fail_addr);
	curr_penalty += penalty_gain;

	/* Step to next instruction */
	newpc = pc + penalty_sz;
	label = newpc -> action;
	NEXT;
    }

UPENALTY:
    {	Penalty penalty_gain = pc[1].penalty;
	logexec(upenalty);
	opc_count(upenalty);

	curr_penalty -= penalty_gain;
	DB_RTS(abs_message ("UPENALTY(%d): back to penalty %d", penalty_gain, curr_penalty));

	/* Step to next instruction */
	newpc = pc + upenalty_sz;
	label = newpc -> action;
	NEXT;
    }

COMMIT:
    {   logexec(commit);
	opc_count(commit);

	if (fp[NR_SUCCS_OFF].arg)	/* continuation taken (SUCCESS)? */
	  { DB_RTS (abs_message ("COMMIT: skipping further alternatives"));
	    GOTO(pc[1].code);		/* skip other alternatives */
	  };

	DB_RTS(abs_message ("COMMIT: trying next alternative"));

	/* Step to next instruction */
	newpc = pc + commit_sz;
	label = newpc -> action;
	NEXT;				/* try other alts for lower penlevel */
    }


/*------------------------------------------------------------------------------
// Instructions for the start rule:
//----------------------------------------------------------------------------*/
SUCCESS:
    {
#ifndef GEN_RTS
	logexec(success);
	opc_count(success);

	fp[NR_SUCCS_OFF].arg++;
	DB_RTS(abs_message ("SUCCESS: "));
	stop_parse_time();

#else /* GEN_RTS */
	logexec(success);
	opc_count(success);
	incr_nr_parses();
#endif /* GEN_RTS */

	/* Step to next instruction */
	newpc = pc + success_sz;
	label = newpc -> action;
	NEXT;
    }

USUCCESS:
    {
	logexec(usuccess);
	opc_count(usuccess);
	DB_RTS(abs_message ("USUCCESS: "));

#ifndef GEN_RTS
#ifndef PMRTS
	stop_print_time();
	stop_printing();
	/* parse_results_start_new(); */
#endif
	start_parse_time();
#endif /* GEN_RTS */

	/* Step to next instruction */
	newpc = pc + usuccess_sz;
	label = newpc -> action;
	NEXT;
    }

END:
    {   StateIndicator new_start_node;

#ifndef GEN_RTS
	stop_parse_time();
	DB_RTS(abs_message ("END: current time is %.3fs.", get_parse_time()));
	print_parsed_sentence();
	parse_results_dump();
	show_totals(trellis);

	/* exit interpreter */
	if (!segment_mode)
	  { /* Not in paragraph-mode, so stop. */
	    new_start_node = NULL;
	  }
	else
	  { /* Paragraph mode, check if we reached the time limit */
	    if (have_time_limit() && have_time_out())
	      { /* we have reached the time limit, see what we can do now...: */
	        if (parse_results_get_nr_parses())
		  { /* we found at least one parse, so start after that one */
	            new_start_node = get_statenode_from_first_parse ();
	            if (new_start_node && state_has_eos_transition (new_start_node))
	              /* we are at the end of the paragraph, so we may stop */
	              new_start_node = NULL;
	          }
		else
	          /* we didn't even find 1 single parsing, bail out */
	          new_start_node = get_shortest_transition (trellis, START_INPUT_STATE);
	      }
	    else
	      { /* We found a good segment, so start after it the next time: */
	        new_start_node = get_statenode_from_first_parse ();
	        if (!new_start_node)
	          new_start_node = get_shortest_transition (trellis, START_INPUT_STATE);
	        else if (state_has_eos_transition (new_start_node))
	          /* we are at the end of the sentence, so we can stop! */
	          new_start_node = NULL;
	      };
	  };

	parse_results_destroy ();
#else /* GEN_RTS */
#ifdef DEBUG_RTS
	choice_admin_dump_table ();
#endif /* DEBUG_RTS */
	choice_admin_destroy ();
#endif /* GEN_RTS */

	abs_free (stack, "agfl_interpret: stack");
	return (new_start_node);
    }

/*------------------------------------------------------------------------------
// Transduction
//----------------------------------------------------------------------------*/
PASS2:
    {
	logexec(pass2);
	opc_count(pass2);

#ifndef GEN_RTS
#ifdef PMRTS
	DB_RTS (abs_message ("PASS2: %s", (return_to_pass2)?": returning to":"entering"));
	if (return_to_pass2)
          { stop_print_time ();
	    stop_printing ();
	  };

	while (!return_to_pass2 || try_update_ambi_stack ())
	  { /*
	       We have to execute PASS2 with the first parse OR
	       we have to reexecute PASS2 with another parse
	    */
#else /* No PMRTS */
	if (no_output)
	  { newpc = pc + pass2_sz;
	    label = newpc -> action;

	    /* initialize print time structure */
	    start_print_time();
	    stop_print_time();
	    NEXT;
	  }
	else
	  { /*
	       Check if it makes sense to execute PASS2
	    */
#endif
	    unsigned startpos = STATE_GET_ENDPOS(START_INPUT_STATE);
	    unsigned endpos = STATE_GET_ENDPOS(i_state);
	    unsigned length = endpos - startpos;
	    incr_nr_parses ();

	    if (current_parse_is_acceptable (curr_penalty, length))
	      { /* This parse can be added */
	        DB_RTS (abs_message ("PASS2: found parse len %d, penalty %d, accepted", length, curr_penalty));
	        parse_result.penalty = curr_penalty;
		parse_result.startpos = startpos;
		parse_result.endpos = endpos;
	        current_parse_set_prio_and_add (curr_penalty, i_state, length);
	        start_print_time ();
	        start_printing ();

	        /* Setup PASS2 frame */
	        sp -= PASS2_FRAME_HEAD_LEN;
	        sp[PASS2_PASS2FP].data = 0;
#ifdef PMRTS
	        /* Return to this instruction, to check for more output */
	        sp[PASS2_FP].pmprod = pmptr;
	        sp[PASS2_RET_ADDR].code = pc;
	        return_to_pass2 = 1;

	        /* Set pmptr to 0; PRINT_SON (0) in root print/trans tree will detect it */
	        pmptr = NULL;
#else
	        sp[PASS2_FP].data = fp;
	        sp[PASS2_RET_ADDR].code = pc + pass2_sz;
#endif
	        pass2fp = sp;
	        newpc = pc[1].code;
	        label = newpc -> action;
	        NEXT; 
	      }
#ifdef PMRTS
	    else
	      { /* force the ambi check */
	        DB_RTS (abs_message ("PASS2: found parse len %d, penalty %d, refused", length, curr_penalty));
		return_to_pass2 = 1;
	      }

#else /* PMRTS */
	    else
 	      { newpc = pc + pass2_sz;
	        label = newpc -> action;

	        /* initialize print time structure */
	        start_print_time();
	        stop_print_time();
	        NEXT;
	      }
#endif
	  } /* } */
#ifdef PMRTS
          /*
	     We left the PASS2 loop, so we continuate with next instruction
	  */
	  return_to_pass2 = 0;
	  newpc = pc + pass2_sz;
	  label = newpc -> action;
	  NEXT; 
#endif
#else /* GEN_RTS */
	gen_output_show();
	newpc = pc + pass2_sz;
	label = newpc->action;
	NEXT;
#endif /* GEN_RTS */
    }

/*-----------------------------------------------------------
// RETRN
//---------------------------------------------------------*/
RETRN:
    {
	DATA* new_pass2fp;
	unsigned nr_formals;

	logexec(retrn);
	opc_count(retrn);

	newpc = pass2fp[PASS2_RET_ADDR].code;
#ifdef PMRTS
	/* get nr_formals (of myself, the son) */
	if (pmptr != NULL) nr_formals = pmptr -> nr_formals;
	else nr_formals = 0;

	/* Restore the posmemo of the father */
	pmptr = pass2fp[PASS2_FP].pmprod;
	DB_RTS(
	    abs_printf ("RETRN: nr_formals = %u ", nr_formals);
	    if (pmptr) {
		int nont_nr = pmptr->nont_nr;
		const char *name;
		if (nont_nr >= 0 && nont_nr < nr_syntax_nonterminals)
		    name = nonterm_names[nont_nr];
		else
		    name = "(outofrange)";
		abs_printf ("to rule %d \"%s\"", nont_nr, name);
	    }
	    abs_printf("\n");
	);
#else
	nr_formals = NR_FORMALS;
	fp = pass2fp[PASS2_FP].data;
	DB_RTS(abs_message ("RETRN: nr_formals = %u", nr_formals));
#endif

	/* Restore the stack frame */
	new_pass2fp = pass2fp[PASS2_PASS2FP].data;
	sp += PASS2_FRAME_HEAD_LEN + nr_formals;
	pass2fp = new_pass2fp;

	label = newpc -> action;
	NEXT;
    }

PUSH_F:
    {
	ARG formal = pc[1].arg;
        /* Hoe kom je hieraan? */
	Value formal_value = pass2fp[PASS2_FRAME_HEAD_LEN + formal + 1].val;
	logexec(push_f);
	opc_count(push_f);
	DB_RTS(abs_message ("PUSH_F: formal == %ld, value == %08x, sp = %p", formal, formal_value.set_par, sp));

	PUSH_VALUE(formal_value);

	newpc = pc + push_f_sz;
	label = newpc->action;
	NEXT;
    }

PUSH_L:
    {
	ARG local = pc[1].arg; 

	logexec(push_l);
	opc_count(push_l);

#ifdef PMRTS
	PUSH_VALUE (posmemo_get_local (pmptr,local));
#else
	PUSH_VALUE (VARIABLE (local+ NR_FORMALS).val);
#endif
	DB_RTS(abs_message ("PUSH_L: local == %ld, value == %08x, sp = %p",
			    local, VARIABLE(local).val.set_par, sp));
	newpc = pc + push_l_sz;
	label = newpc->action;
	NEXT;
    }

/*-----------------------------------------------------------
// PRINT_SON
//---------------------------------------------------------*/
PRINT_SON:
    {	ARG son_nr = pc[1].arg;
	logexec (print_son);
	opc_count (print_son);
#ifdef PMRTS
	DB_RTS (abs_message ("PRINT_SON: curr pmptr = %p, sonnr = %ld, nr_sons = %d, stack size = %d", pmptr, son_nr, pmptr ? pmptr->nr_sons : 1, sp - stack));
#else
	DB_RTS (abs_message ("PRINT_SON: sonnr = %ld", son_nr));
#endif

	/* create a new pass2 stack frame */
	sp -= PASS2_FRAME_HEAD_LEN;
	sp[PASS2_PASS2FP].data = pass2fp;
#ifdef PMRTS
        /* to remember our current posmemo pointer */
	sp[PASS2_FP].pmprod = pmptr;
#else
	sp[PASS2_FP].data = fp;
#endif
	sp[PASS2_RET_ADDR].code = pc + print_son_sz;

	pass2fp = sp;

#ifdef PMRTS
	/* pick up posmemo for either the son or the root rule */
	if (pmptr != NULL) pmptr = posmemo_get_son (pmptr,son_nr);
	else pmptr = SON_FP(0)[PMPRODPTR].pmprod;	/* Root rule */

	/* Check for ambiguity: we may pass this point more than once */
	if (pmptr -> prime != NULL)
	   { PosMemo sibling;
	     assert (pmptr -> prime == pmptr);
	     if (try_lookup_sibling_in_ambi_stack (pmptr, &sibling)) pmptr = sibling;
	     else push_ambi_stack (pmptr -> prime);
	   };

	/* continue with the transduction of the son */
	DB_RTS(abs_message ("Using %p as PMPROD to PRINT SON (rule %d \"%s\")", pmptr, pmptr->nont_nr, nonterm_names[pmptr->nont_nr]));
	newpc = posmemo_get_pass2 (pmptr); 
#else
	fp = SON_FP(son_nr);

	DB_RTS(abs_message ("printing son %ld: \"%s\"", son_nr, nonterm_names[NONT_NR]));
	DB_RTS(abs_message ("jump to 2nd pass code @%p", fp[PASS2_CODE].code));

	newpc = fp[PASS2_CODE].code;
#endif
	label = newpc -> action;
	NEXT;
    }

/*-----------------------------------------------------------
// Print the begin and end of an alternative
// Only used in tree output
//
// if labelled bracket output wanted, print open and closed
// curly braces; otherwise use indentation
//---------------------------------------------------------*/
PRINT_ABEG:
    { char* alt_text = pc[1].val.text_par;

      logexec(print_abeg);
      opc_count(print_abeg);
      DB_RTS(abs_message ("PRINT_ABEG: alt_text = %s", alt_text));

      if (label_bracket)
         { if (alt_text && parsing_stats_option)
	      current_parse_printf ("{%s:", alt_text);
	   else current_parse_add_char('{');
	 }
      else
	 { if (alt_text && parsing_stats_option)
	      current_parse_printf (" <alt %s>\n", alt_text);
	   else current_parse_add_char('\n');

	   indent += 2;
         };

      newpc = pc + print_abeg_sz;
      label = newpc -> action;
      NEXT;
    }

/*----------------------------------------------------------
// PRINT_AEND: print end alternative.
//--------------------------------------------------------*/
PRINT_AEND:
    { logexec(print_aend);
      opc_count(print_aend);
      DB_RTS(abs_message ("PRINT_AEND: "));

      if (label_bracket) current_parse_add_char('}');
      else indent -= 2;

      newpc = pc + print_aend_sz;
      label = newpc -> action;
      NEXT;
    }

//---------------------------------------------------------
// PRINT_PBEG, PRINT_PSEP and PRINT_PEND serve to print
// the affix list with a production i.e. they print a
// '(', ',' and ')'
//-------------------------------------------------------*/
PRINT_PBEG:
    { logexec(print_pbeg);
      opc_count(print_pbeg);
      DB_RTS(abs_message ("PRINT_PBEG: "));

      current_parse_add_char('(');
	
      newpc = pc + print_pbeg_sz;
      label = newpc -> action;
      NEXT;
    }

PRINT_PSEP:
    { logexec(print_psep);
      opc_count(print_psep);
      DB_RTS(abs_message ("PRINT_PSEP: "));

      current_parse_add_char(',');
      if (!label_bracket) current_parse_add_char(' ');
	
      newpc = pc + print_psep_sz;
      label = newpc->action;
      NEXT;
    }

PRINT_PEND:
    { logexec(print_pend);
      opc_count(print_pend);
      DB_RTS(abs_message ("PRINT_PEND: "));

      current_parse_add_char(')');
	
      newpc = pc + print_pend_sz;
      label = newpc->action;
      NEXT;
    }

/*--------------------------------------------------
// Print a text value
//------------------------------------------------*/
PRINT_VAL:
    { char* text = pc[1].val.text_par;

      logexec(print_val);
      opc_count(print_val);
      DB_RTS(abs_message ("PRINT_PVAL: text = %s", text));

      if (transduce_option) current_parse_add_string(text);
      else abs_bug ("agfl_interpret", "unexpected PRINT_VAL in tree");

      newpc = pc + print_val_sz;
      label = newpc->action;
      NEXT;
    }

/*-----------------------------------------------------------
// PRINT_TERM: print the terminal belonging to a transition
//---------------------------------------------------------*/
PRINT_TERM:
    {
#ifdef PMRTS
	/* MS: Hier zou je een typecheck kunnen doen... */
	Transition *p_transition = ((DATA)posmemo_get_variable(pmptr,pc[1].arg)).input_transition;
#else
	Transition *p_transition = VARIABLE(pc[1].arg).input_transition;
#endif
	char *text = p_transition -> text;
	DB_RTS(abs_message ("PRINT_TERM: text = %s", text));

	logexec(print_term);
	opc_count(print_term);

	if (transduce_option)
          { current_parse_add_string(text);
	    if (IS_LASTPART(p_transition))
	        current_parse_add_char(' ');
	  }
	else
          { int prefix = 0;
	    int suffix = 0;

	    switch (get_transition_lex_type(p_transition))
	      { case Infix:	suffix = 1; 
	        case Prefix:	prefix = 1;
	            		break;
	        case Suffix:	suffix = 1;
	        default:	break;
	      };

	    if (!label_bracket) current_parse_add_space(indent);

	    if (suffix) current_parse_add_string ("\"-");
	    else current_parse_add_char ('\"');
	    current_parse_add_string (text);

	    if (prefix) current_parse_add_string ("-\"");
	    else current_parse_add_char ('\"');

	    if (!label_bracket) current_parse_add_char('\n');
	  };

	newpc = pc + print_term_sz;
	label = newpc -> action;
	NEXT;
    }

/*-------------------------------------------------------------
// PRINT_LEX: print the text belonging to a lexicon transition
//-----------------------------------------------------------*/
PRINT_LEX:
    {   /* Pick corresponding transition from saved one in stack frame */
	char *text;
#ifdef PMRTS
	/* MS: Hier zou je een typecheck kunnen doen... */
	Transition* p_transition = (Transition*)posmemo_get_son(pmptr,pc[1].arg);
#else // !PMRTS
	Transition* p_transition = (Transition*) SON_FP(pc[1].arg);
#endif // PMRTS

	logexec (print_lex);
	opc_count (print_lex);
	text = p_transition -> text;
	DB_RTS(abs_message ("PRINT_LEX: text = %s", text));

	if (transduce_option)
	  { current_parse_add_string (text);
	    if (IS_LASTPART(p_transition))
	      current_parse_add_char(' ');
	  }
	else
	  { int prefix = 0;
	    int suffix = 0;

	    switch (get_transition_lex_type(p_transition))
	      { case Infix:  suffix = 1;
	        case Prefix: prefix = 1; break;
	        case Suffix: suffix = 1; break;
	        default: break;
	      };

	    if (!label_bracket)
	      current_parse_add_space(1);

	    if (suffix)
	      current_parse_add_string("\"-");
	    else current_parse_add_char('\"');
	    current_parse_add_string(text);

	    if (prefix)
	      current_parse_add_string("-\"");
	    else current_parse_add_char('\"');

	    if (!label_bracket)
	      current_parse_add_char('\n');
	  };

	newpc = pc + print_lex_sz;
	label = newpc->action;
	NEXT;
    }

/*-------------------------------------------------------------
// PRINT_RE: print the text belonging to a regular expression
// PRINT_OTHER: print the text not matched by anything
// For the time being we use the same code.
//-----------------------------------------------------------*/
PRINT_RE:
PRINT_OTHER:
    {   char *text;
	Transition *p_transition;
	ARG var = pc[1].arg;

	logexec(print_re);
	opc_count(print_re);

	/* Pick transition from saved location in frame */
#ifdef PMRTS
	if (var < pmptr -> nr_formals)
	  p_transition = pass2fp[PASS2_FRAME_HEAD_LEN + var + 1].input_transition;
	else p_transition = ((DATA)(posmemo_get_variable(pmptr,var))).input_transition;
#else // !PMRTS
	p_transition = VARIABLE(var).input_transition;
#endif // PMRTS
	text = p_transition -> text;
	DB_RTS(abs_message ("PRINT_RE(or OTHER): text = %s", text));

	if (transduce_option)
          { current_parse_add_string (text);
	    if (IS_LASTPART (p_transition))
	      current_parse_add_char (' ');
	  }
	else
	  { if (!label_bracket)
	      current_parse_add_space (indent);

	    current_parse_add_char ('\"');
	    current_parse_add_string (text);

	    if (!label_bracket)
	      current_parse_add_string ("\"\n");
	    else current_parse_add_char ('\"');
	  };

	/* Step to next instruction */
	newpc = pc + print_re_sz;
	label = newpc -> action;
	NEXT;
    }

/*---------------------------------------------------------------
// PRINT_POS: print a position
//-------------------------------------------------------------*/
PRINT_POS:
    {	/* Pick up argument */
	ARG local = pc[1].arg;
	logexec (print_pos);
	opc_count (print_pos);

	/* If transducing, print the saved position */
	if (transduce_option)
	  { /* Pick up text from frame or posmemo and print it */
#ifdef PMRTS
	    Value val = posmemo_get_variable (pmptr, local);
#else
	    Value val = VARIABLE(local).val;
#endif /* PMRTS */
	    if (val.text_par == NULL)
	      current_parse_add_string ("chaos awaits the pointer in posmemo");
	    else current_parse_add_string (val.text_par);
	  }
	else
	  { current_parse_add_space (indent);
	    current_parse_add_string ("$POS\n");
	  };

	/* Step to next instruction */
	newpc = pc + print_re_sz;
	label = newpc -> action;
	NEXT;
    }

/*---------------------------------------------------------------
// PRINT_PEN: print a penalty
//-------------------------------------------------------------*/
PRINT_PEN:
    {	/* Pick up penalty */
	int pen = pc[1].arg;

	logexec(print_pen);
	opc_count(print_pen);
	DB_RTS(abs_message ("PRINT_PEN: pen = %d", pen));

	if (!label_bracket && !transduce_option)
	  { current_parse_add_space(indent);
	    if (pen == 1) current_parse_add_string("$PENALTY");
	    else
	      { current_parse_add_string("$PENALTY(");
		current_parse_add_int(pen);
		current_parse_add_char(')');
	      };
	    current_parse_add_char('\n');
	  };

	/* Step to next instruction */
	newpc = pc + print_pen_sz;
	label = newpc -> action;
	NEXT;
    }

/*---------------------------------------------------------------
// PRINT_N: print nonterminal name
//-------------------------------------------------------------*/
PRINT_N:
    {	/* Pick up argument */
	char* text = pc[1].val.text_par;

	logexec(print_n);
	opc_count(print_n);
	DB_RTS(abs_message ("PRINT_N: nont = %s", text));

	if (!label_bracket)
	  current_parse_add_space (indent);
	current_parse_add_string (text);

	/* Step to next instruction */
	newpc = pc + print_n_sz;
	label = newpc -> action;
	NEXT;
    }

PRINT_IF:
    {	/* Pick up argument */
	ARG formal = pc[1].arg;
	Value val = pass2fp[PASS2_FRAME_HEAD_LEN + formal + 1].val;
	DB_RTS(abs_message ("PRINT_IF: int formal = %d", val.int_par));

	logexec (print_if);
	opc_count (print_if);

	print_integer_affix (val.int_par, 1);

	/* Step to next instruction */
	newpc = pc + print_if_sz;
	label = newpc -> action;
	NEXT;
    }

PRINT_IL:
    { 	/* Pick up argument */
	ARG local = pc[1].arg;
#ifdef PMRTS
	Value val = posmemo_get_local(pmptr,local);
#else
	Value val = VARIABLE(NR_FORMALS + local).val;
#endif

	logexec(print_il);
	opc_count(print_il);
	DB_RTS(abs_message ("PRINT_IL: int local = %d", val.int_par));

	print_integer_affix (val.int_par, 1);

	/* Step to next instruction */
	newpc = pc + print_il_sz;
	label = newpc -> action;
	NEXT;
    }

PRINT_SF:
    {
	ARG formal = pc[1].arg;
	Value val = pass2fp[PASS2_FRAME_HEAD_LEN + formal + 1].val;
	ARG domain = pc[2].arg;
	DB_RTS(abs_message ("PRINT_SF: set formal = %08x", val.set_par));

	logexec(print_sf);
	opc_count(print_sf);

	print_set_affix(val.set_par, domain, 1);

	/* Step to next instruction */
	newpc = pc + print_sf_sz;
	label = newpc -> action;
	NEXT;
    }

PRINT_SL:
    {
	ARG local = pc[1].arg;
#ifdef PMRTS
	Value val = posmemo_get_local(pmptr,local);
#else
	Value val = VARIABLE(NR_FORMALS + local).val;
#endif
	ARG domain = pc[2].arg;
	DB_RTS(abs_message ("PRINT_SL: set local = %08x", val.set_par));

	logexec(print_sl);
	opc_count(print_sl);

	print_set_affix(val.set_par, domain, 1);

	newpc = pc + print_sl_sz;
	label = newpc->action;
	NEXT;
    }

PRINT_TF:
    {
	ARG formal = pc[1].arg;
	Value val = pass2fp[PASS2_FRAME_HEAD_LEN + formal + 1].val;

	logexec(print_tf);
	opc_count(print_tf);
	DB_RTS(abs_message ("PRINT_TF: text formal = %s", val.text_par));

	print_text_affix(val.text_par, 1);

	newpc = pc + print_tf_sz;
	label = newpc->action;
	NEXT;
    }

PRINT_TL:
    {
	ARG local = pc[1].arg;
#ifdef PMRTS
	Value val = posmemo_get_local(pmptr,local);
#else
	Value val = VARIABLE(NR_FORMALS + local).val;
#endif

	logexec(print_tl);
	opc_count(print_tl);
	DB_RTS(abs_message ("PRINT_TL: text local = %s", val.text_par));

	print_text_affix(val.text_par, 1);

	newpc = pc + print_tl_sz;
	label = newpc->action;
	NEXT;
    }

PRINT_VF:
    {
	logexec(print_vf);
	opc_count(print_vf);

	assert(!"PRINT_VF not implemented");

	newpc = pc + print_vf_sz;
	label = newpc->action;
	NEXT;
    }

PRINT_VL:
    {
	logexec(print_vl);
	opc_count(print_vl);

	assert(!"PRINT_VL not implemented");

	newpc = pc + print_vl_sz;
	label = newpc->action;
	NEXT;
    }


/*------------------------------------------------------------------------------
// Generateve grammar instructions:
//----------------------------------------------------------------------------*/
CHOICE:
#ifndef GEN_RTS
    {
	logexec(choice);
	opc_count(choice);
	goto ILLEGAL;
    }
#else /* GEN_RTS */
#define NR_TRIES_PER_ALTGROUP 1
    {
	ARG choice_nr = pc[1].arg;
	ARG nr_alts = pc[2].arg;

	logexec(choice);
	opc_count(choice);
	ALT_DEPTH_CHECK;

	if (get_nr_parses()) {
	    /* a sentence is generated, we can stop */
	    choice_admin_remove(alt_depth, choice_nr);
	    chosen_freq = -2;
	} else if (choice_admin_get_nr_tries(alt_depth, choice_nr) >= (NR_TRIES_PER_ALTGROUP * nr_alts)) {
	    /* nr_alts == nr of tries, which all failed, so fail */
	    choice_admin_remove(alt_depth, choice_nr);
	    chosen_freq = -3;
	} else {
	    /* failed to generate a sentence, but not everything is tried */
	    PUSH_ADDR(pc);
	    chosen_freq = rand() % nr_alts;
//	    chosen_freq = choice_admin_get_nr_tries(alt_depth, choice_nr);
	    choice_admin_incr_nr_tries(alt_depth, choice_nr);
	}

	newpc = pc + choice_sz;
	label = newpc->action;
	NEXT;
    }
#endif /* GEN_RTS */

UCHOICE:
#ifndef GEN_RTS
    {
	logexec(uchoice);
	opc_count(uchoice);
	goto ILLEGAL;
    }
#else /* GEN_RTS */
    {	ARG choice_nr = pc[1].arg;

	logexec(uchoice);
	opc_count(uchoice);

	if (((chosen_freq == -2) || (chosen_freq == -3)) &&
	     !choice_admin_get_nr_tries(alt_depth, choice_nr))
	  { /* Step to next instruction */
	    newpc = pc + uchoice_sz;
	    label = newpc->action;
	    NEXT;
	  }
	else
	  { CODE* choice_addr;
	    POP_ADDR(choice_addr);
	    assert(choice_addr);
	    GOTO(choice_addr);
	  };
    }
#endif /* GEN_RTS */

ALT_MARKER:
#ifndef GEN_RTS
    {
	logexec(alt_marker);
	opc_count(alt_marker);
	goto ILLEGAL;
    }
#else /* GEN_RTS */
    {	ARG my_freq = 1;
	CODE* next_alt = pc[1].code;

	logexec(alt_marker);
	opc_count(alt_marker);
	ALT_DEPTH_CHECK;

	if ((chosen_freq < my_freq) && (chosen_freq >= 0))
	  { /* we're gonna generate for this alternative */
	    chosen_freq = -1;
	    ALT_DEPTH_INCREASE;

	    /* Step to next instruction */
	    newpc = pc + alt_marker_sz;
	    label = newpc -> action;
	    NEXT;
	  }
	else if (chosen_freq >= 0)
	  { /* this alternative is not chosen, goto next one */
	    chosen_freq -= my_freq;
	  }
	/* else we're done, so skip this alternative */

	/* Skip to next alternative */
	newpc = next_alt;
	label = newpc -> action;
	NEXT;
    }
#endif /* GEN_RTS */

UALT_MARKER:
#ifndef GEN_RTS
    {
	logexec(ualt_marker);
	opc_count(ualt_marker);
	goto ILLEGAL;
    }
#else /* GEN_RTS */
    {
	logexec(ualt_marker);
	opc_count(ualt_marker);
	ALT_DEPTH_CHECK;

	ALT_DEPTH_DECREASE;

	newpc = pc + ualt_marker_sz;
	label = newpc->action;
	NEXT;
    }
#endif /* GEN_RTS */

/*-------------------------------------------------------------------------
// Instruction: ROOT_CREATE (nr formals, nr locals, nr sons)
// Description: Setup an initial stack frame 
// Note that the frame head of this stack frame is never to be used
//-----------------------------------------------------------------------*/
ROOT_CREATE:
#ifdef PMRTS
    { ARG nr_formals = pc[1].arg;
      ARG nr_locals = pc[2].arg;
      ARG nr_sons = pc[3].arg;
      int i;

      logexec(root_create);
      opc_count(root_create);

      sp[0].data = fp;	        /* store old fp */
      fp = sp;

      sp -= nr_formals + nr_locals + nr_sons + VAR_OFFSET;
      NR_FORMALS = nr_formals;
      NR_LOCALS = nr_locals;
      NR_SONS = nr_sons;
      DB_RTS(NONT_NR = 0);	// for in FAIL.

      START_INPUT_STATE = i_state;
      fp[NR_SUCCS_OFF].arg = 0;
      fp[NR_SONS_DONE].arg = 0;
      fp[PENORIG].penalty = curr_penalty;

      /*
	 Initialise the locals and sons to 0, since otherwise equal_posmemo()
         may look at uninitialised values sometimes (and fail to detect
         identity). This happens because not every alternative uses (sets) the
         full set of locals and sons so some may remain uninitialised.
      */
      for (i = 0; i < nr_locals + nr_sons; i++)
	VARIABLE(nr_formals + i).arg = 0;

      DB_RTS(abs_message ("ROOT_CREATE: start_input_state = %p, sp = %p, stack bottom = %p", i_state, sp, stack));
      fp[PMPRODPTR].pmprod = NULL;

      /* Step to next instruction */
      newpc = pc + root_create_sz;
      label = newpc -> action;
      NEXT;
    }
#else /* PMRTS */
    { goto CREATE;
    }
#endif /* PMRTS */

ROOT_CALL:
#ifdef PMRTS
    {
	ARG rule_nr = pc[1].arg;
	CODE* jmp_point = pc[2].code;
	CODE* fail_addr = pc[3].code;

	logexec(root_call);
	opc_count(root_call);

	DB_RTS(abs_message ("ROOT_CALL: current time is %.3fs.", get_parse_time()));
	if (posmemo_is_unknown (START_INPUT_STATE, rule_nr)) /* first try */
	   posmemo_set_blocked(START_INPUT_STATE, rule_nr);

	sp -= FRAME_HEAD_LEN;
	sp[FAIL_ADDR_OFF].code = fail_addr;
	sp[SUCC_ADDR_OFF].code = pc + root_call_sz;

	SON_FP(fp[NR_SONS_DONE].arg) = sp;
	fp[NR_SONS_DONE].arg++;
	sp[NONT_NR_OFF].arg = rule_nr;
	DB_RTS(abs_message ("Root call: Starting parse, penalty = %d", curr_penalty));

	/* Jump to next instruction */
	newpc = jmp_point;
	label = newpc -> action;
	NEXT;
    }
#else /* PMRTS */
    {
	logexec(root_call);
	opc_count(root_call);

	goto ILLEGAL;
    }
#endif /* PMRTS */

/*-------------------------------------------------------------------------
// Instruction: (U)MARK_LREC 
// Description: (Un)mark that calls for the current nonterminal and input
//		state should fail because they are (possibly hidden) left
//		recursive calls
//-----------------------------------------------------------------------*/
MARK_LREC:
    {	/* Pick up input state ptr */
	StateIndicator input_state = START_INPUT_STATE;
	logexec(mark_lrec);
	opc_count(mark_lrec);
	DB_RTS(abs_message("MARK_LREC for nont %ld at pos %u", NONT_NR, STATE_POS (input_state)));

#ifdef PMRTS
	/* Check if we are unmarked; panic if so */
#ifdef DEBUG_RTS
	if (input_state -> lrec_markers[NONT_NR])
	  abs_abort ("MARK_LREC", "Rule %ld already blocked for left recursion", NONT_NR);
#endif /* DEBUG_RTS */
	input_state -> lrec_markers[NONT_NR] = 1;
#else
	goto ILLEGAL;
#endif /* PMRTS */

	/* Step to next instruction */
	newpc = pc + mark_lrec_sz;
	label = newpc -> action;
	NEXT;
    }

UMARK_LREC:
    {	/* Pick up input state ptr */
	StateIndicator input_state = START_INPUT_STATE;
	CODE* redo_addr = pc[1].code;
	logexec(umark_lrec);
	opc_count(umark_lrec);
	DB_RTS (abs_message ("UMARK_LREC for rule %ld at start pos %u", NONT_NR,
			     STATE_POS (input_state)));

#ifdef PMRTS
	/* Check if we are marked; panic if so */
	if (input_state -> lrec_markers[NONT_NR])
	  { /* Undo the marking and redo all alternatives */
	    input_state -> lrec_markers[NONT_NR] = 0;
	    DB_RTS (abs_message ("UMARK_LREC unmarked rule %ld and jumps back", NONT_NR));
	    GOTO (redo_addr);
	  };
#else
	goto ILLEGAL;
#endif /* PMRTS */

	/* Step to next instruction */
	newpc = pc + umark_lrec_sz;
	label = newpc -> action;
	NEXT;
    }

#ifdef DEBUG_RTS
TRACE:
    {	/* Step to next instruction */
	newpc = pc + trace_sz;
	label = newpc -> action;
	NEXT;
    }
#else /* DEBUG_RTS */
TRACE:
    {
	logexec(trace);
	opc_count(trace);

	goto ILLEGAL;
    }
#endif /* DEBUG_RTS */

POSITION:
    {
	ARG position = pc[1].arg;

	alternatives_profile_table[position]++;
	/* abs_message ("Alternative  %d [%ld]:", (int) position,
			(long) alternatives_profile_table[position]);
	*/

	/* Step to next instruction */
	newpc = pc + position_sz;
	label = newpc -> action;
	NEXT;
    }


/*------------------------------------------------------------------------------
// Illegal instructions
//----------------------------------------------------------------------------*/
ILLEGAL:
    { abs_bug ("agfl_interpret", "rts: illegal instruction");
    }

  abs_free (stack, "agfl_interpret:ILLEGAL");
  return NULL;
}

/* Write parse results in the .prd file */
void write_profile_table()
{ unsigned position;

  for (position = 0; position < nr_positions; position++)
    profile_printf ("%d:%ld\n", (int) position, (long) alternatives_profile_table[position]);
}


/*------------------------------------------------------------------------------
// Parsing interface
//----------------------------------------------------------------------------*/
static void reset_parser (Trellis* trellis)
{
    reset_parse_result ();
    parse_results_destroy ();
    parse_results_init ();
#ifdef GEN_RTS
    parse_results_start_new ();
#endif /* GEN_RTS */
}

StateIndicator parse_trellis (Trellis* trellis, StateIndicator start_node)
{ StateIndicator result;
  reset_parser (trellis);

  if (have_time_limit ()) {
#ifdef DEBUG_TIME_OUT
	abs_message ("setting timeout to %ld secs", max_parsetime);
#endif /* DEBUG_TIME_OUT */

	set_time_limit();
    }

#ifdef DEBUG_NEGMEMO
  if (neg_memo_option || directors_option)
    print_negmemo_table(trellis, 0);
#endif /* DEBUG_NEGMEMO */

  result = agfl_interpret (Run, trellis, start_node);
  if (count_option)
    agfl_interpret (Statistics, NULL, NULL);
  return (result);
}

/*------------------------------------------------------------------------------
// Prelude and postlude of module
//----------------------------------------------------------------------------*/
void init_parser ()
{
#ifdef PMRTS
   init_ambi_stack ();
   posmemo_init ();
#endif /* PMRTS */
   agfl_interpret(Translate, NULL, NULL);

   /* agfl_interpret (Statistics, NULL, NULL);*/
}

void end_parser ()
{
#ifdef PMRTS
   posmemo_done ();
   finish_ambi_stack ();
#endif /* PMRTS */
   write_profile_table ();
}
