diff anagram/agcore/bpe3.cpp @ 0:13d2b8934445

Import AnaGram (near-)release tree into Mercurial.
author David A. Holland
date Sat, 22 Dec 2007 17:52:45 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/anagram/agcore/bpe3.cpp	Sat Dec 22 17:52:45 2007 -0500
@@ -0,0 +1,3115 @@
+/*
+ * AnaGram, A System for Syntax Directed Programming
+ * Copyright 1993-2002 Parsifal Software. All Rights Reserved.
+ * See the file COPYING for license and usage terms.
+ *
+ * bpe3.cpp - build parse engine, rev. 3
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <time.h>
+#include "port.h"
+
+#include "agarray.h"
+#include "agbaltree.h"
+#include "agcstack.h"
+#include "agdict.h"
+#include "agstring.h"
+#include "arrays.h"
+#include "assert.h"
+#include "build.h"
+#include "bpe3.h"
+#include "bpu.h"
+#include "binsort.h"
+#include "cd.h"
+#include "config.h"
+#include "csexp.h"
+#include "dict.h"
+#include "error.h"
+#include "file.h"
+#include "keyword.h"
+#include "minmax.h"
+#include "myalloc.h"
+#include "operations.h"
+#include "p.h"
+#include "q1a.h"
+#include "q1glbl.h"
+#include "q5.h"
+#include "rproc.h"
+#include "rpz.h"
+#include "rule.h"
+#include "symbol.h"
+#include "stacks.h"
+#include "token.h"
+#include "tsd.h"
+#include "ut.h"
+#include "version.h"
+
+//#define INCLUDE_LOGGING
+#include "log.h"
+
+
+AgStack<CSegment> extensionStack;
+AgStack<CSegment> cSegmentStack;
+int nPrologueSegments;
+
+
+static const char *copyrightMessage = "copyright";
+
+static const char *control_block_type = "$_pcb_type";
+static const char *structNameTemplate = "%_pcb_struct";
+static const char *input_type_name    = "$_it_type"; /* dp(input_type_name); */
+static const char *typedef_name       = "$_token_type";
+static const char *value_stack_type   = "$_vs_type";
+
+
+
+static unsigned    *rproc, nrproc;
+static void         define_my_macros(const char *);
+static int          really_old_style;
+
+static int          actual_stack_size = 0;
+static time_t       infile_time_stamp;
+string_dict        *my_macros = NULL;
+static AgString     pe_file_name;
+int                 pe_line_count;
+
+static const char  *LOCUS = "";
+static const char  *VOIDB  = "void ";
+static const char  *constWord = "const ";
+static Cast         grammar_type;
+static int          input_token_type = 0;
+static AgString     ln_file_name;
+static AgString     ln_pe_name;
+static unsigned     max_engine_index = 0;
+static unsigned     max_pn = 0;
+static const char  *missing_diagnostic = "Missing %s";
+static const char  *nearWord  = "near ";
+static const int    parserLineLength = 76;
+static AgString     sfn;
+static const char  *unexpected_diagnostic = "Unexpected %s";
+static const char  *voidWord   = "void";
+
+char buildErrorMsg[80] = "";
+
+static int parserWriteError;
+static int headerWriteError;
+
+static AgBalancedTree< Triple<int> > anomalyCases;
+
+typedef enum {
+  accept_key,
+  set_key,
+  jmp_key,
+  end_key,
+  no_match_key,
+  cf_accept_key,
+  cf_set_key,
+  cf_end_key
+} key_words;
+
+
+static void gen_input_code(void) {
+  char modes[40];
+  const char *ps;
+
+  write_code_segment(lines_and_columns ? "init col" : "init nocol");
+  strcpy(modes, pointer_input ? " ptr" : " char");
+  if (event_driven) {
+    strcat(modes," sink");
+  }
+  //if (key_dict->nsx > 1) {
+  //  strcat(modes," key");
+  //}
+  if (Keyword::count() > 1) {
+    strcat(modes," key");
+  }
+  sss("init macros");
+  ass(modes);
+  acs(0);
+  define_my_macros(string_base);
+  rcs();
+  select_write_fragment("init", modes, "");
+
+  ps = lines_and_columns ? " col" : "";
+  sss("trk macros");
+  ass(modes);
+  ass(ps);
+  acs(0);
+  define_my_macros(string_base);
+  rcs();
+  select_write_fragment("trk", modes, ps);
+  ps = "";
+  sss("get");
+  ass(modes);
+  ass(ps);
+  acs(0);
+  define_my_macros(string_base);
+  rcs();
+}
+
+static void stack_null_rules(int f) {
+  int *rtl = ibnfs + ibnfb[f];
+  int nrt = ibnfn[f];
+  while (nrt-- && !map_token_number[rtl[nrt]].subgrammar) {
+    xws(rtl[nrt]);
+  }
+}
+
+static int check_anomaly(int sn, int f, int n, int tn) {
+  LOGSECTION("check_anomaly");
+  LOGV(sn) LCV(f) LCV(n) LCV(tn);
+
+  state_number_map *sp = &map_state_number[sn];
+  const int *rtl;
+  unsigned nrt;
+  unsigned i;
+
+  if (anomalyCases.insert(Triple<int>(sn, f, n))) {
+    return 0;
+  }
+
+  if (n) {
+    unsigned *p = lstptr(*sp, previous_states);
+    int nt = sp->n_previous_states;
+
+    n--;
+    while (nt--) {
+      int ns = *p++;
+      int rs = check_anomaly(ns, f, n, tn);
+      if (rs) {
+	return rs;
+      }
+    }
+    return 0;
+  }
+
+  rtl = ibnfs + ibnfb[f];
+  nrt = ibnfn[f];
+  iws();
+  while (nrt-- && !map_token_number[rtl[nrt]].subgrammar) {
+    aws(rtl[nrt]);
+  }
+  for (i = 0; i < (unsigned) tis(); i++) {
+    int t = list_base[i];
+    const unsigned *px = lstptr(*sp, completions);
+    int kn = sp->n_completions;
+    int flag = 0;
+
+    while (kn--) {
+      const int rtk = *px++, g = *px++;
+      int rs;
+      if (rtk != t) {
+	continue;
+      }
+      rs = check_anomaly(sn, g, Rule(g)->length()-1, tn);
+      if (rs) {
+	return rws(),rs;
+      }
+      flag++;
+      break;
+    }
+    if (flag) {
+      continue;
+    }
+    px = lstptr(*sp, gotos);
+    kn = sp->n_gotos;
+    while (kn--) {
+      int rtk = *px++, s = *px++;
+      int fx;
+      unsigned *rp;
+      unsigned nr;
+
+      if (rtk != t) {
+	continue;
+      }
+      if (shift_token(tn, s)) {
+	break;
+      }
+      Rule rule = ruleReducedBy(tn, s);
+      if (rule.isNotNull()) {
+        fx = rule->length();
+        if (fx) {
+          int rs = check_anomaly(sn, rule, fx-1, tn);
+          if (rs) {
+	    return rws(), rs;
+	  }
+          continue;
+        }
+        stack_null_rules(rule);
+        continue;
+      }
+      sp = &map_state_number[s];
+      nr = sp->n_completed_forms;
+      if (nr == 0) {
+	return rws(), s;
+      }
+      rp = lstptr(*sp, completed_forms);
+      for (i = 0; i < nr; i++) {
+        fx = Rule(rp[i])->length();
+        if (fx) {
+          int rs = check_anomaly(sn, rp[i], fx-1, tn);
+          if (rs) {
+	    return rws(),rs;
+	  }
+          continue;
+        }
+        stack_null_rules(rp[i]);
+      }
+    }
+  }
+  rws();
+  return 0;
+}
+
+
+/*
+ * reducing_token(tn, sn) returns 0 if tn is not a reducing token in state sn.
+ * Otherwise it returns the number of the rule it reduces.
+ */
+
+static void check_keys(int sn) {
+  LOGSECTION("check_keys");
+  LOGV(sn);
+  int kl = map_state_number[sn].key_list;
+  unsigned *token_list = lstptr(map_state_number[sn], t_actions);
+  unsigned nt = map_state_number[sn].n_actions;
+  int k;
+  const int *kp;
+  int nk;
+
+  if (kl == 0) {
+    /* no keywords in this state; return */
+    return;
+  }
+
+  AgBalancedTree<AgArray<int> > parseErrorTree;
+  AgBalancedTree<AgArray<int> > noParseErrorTree;
+
+  //LOGSECTION("check_keys");
+  kp = dict_str(key_list_dict, kl);     /* get list of keyword tokens */
+  nk = *kp++ - 1;                       /* length of list */
+  LOGV(sn) LCV(nt) LCV(nk);
+  for (k = 0; k < nk; k++) {            /* for each keyword token */
+    int flag = 0;
+    unsigned t;
+    unsigned char *ks;
+    Token keywordToken = kp[k];
+    //unsigned kf = reducing_token(kp[k], sn);
+    Rule rule = ruleReducedBy(keywordToken, sn);
+    int rs;
+    int rt;
+    int i;
+    LOGSECTION("keyword loop");
+    LOGV(k) LCV(keywordToken) LCV(rule);
+
+    /* if kp[k] doesn't reduce anything or is marked anomalous, continue */
+
+    //if (kf == 0 || map_token_number[kp[k]].reserved_word) {
+    //  continue;
+    //}
+    if (rule.isNull() || keywordToken->reserved_word) {
+      continue;
+    }
+
+    /* kf is non-zero, therefore kp[k] reduces rule kf. */
+
+    Keyword key = keywordToken->key;   /* get actual key index */
+    KeywordDescriptor &keyDescriptor(key);
+    //rt = key->reserve;
+    rt = keyDescriptor.reserve;
+    LOGV(rt);
+
+    //ks = key_string(keywordToken);
+    //ks = (unsigned char *) key->string.pointer();
+    ks = (unsigned char *) keyDescriptor.string.pointer();
+    LOGV(ks) LCV(key);
+    AgStack<int> tokenList;
+    unsigned char *ksp = ks;
+    while (*ksp) {
+      tokenList.push(map_char_number[*ksp++ - min_char_number].token_number);
+    }
+    int listSize = tokenList.size();
+    LOGV(listSize);
+    AgArray<int> tokenArray(listSize);
+    while(listSize--) {
+      tokenArray[listSize] = tokenList[listSize];
+    }
+    if (parseErrorTree.includes(tokenArray)) {
+      continue;
+    }
+
+    //if (keyword_problem(sn, ks, key) < 0) {
+    //  continue;
+    //}
+    if (!noParseErrorTree.includes(tokenArray)) {
+      if (keyword_problem(sn, tokenArray, key) < 0) {
+        parseErrorTree.insert(tokenArray);
+        continue;
+      }
+      noParseErrorTree.insert(tokenArray);
+    }
+
+    t = map_char_number[*ks - min_char_number].token_number;
+
+/*  if (string_cannot_be_shifted((unsigned char *)ks, sn)) continue; */
+
+    LOGV(nt);
+    for (i = 0; flag == 0 && (unsigned) i < nt; i++) {
+      const unsigned char *kks;
+      unsigned length;
+      int rts;
+
+      //if (reducing_token(token_list[i],sn) == kf) continue;
+      if (ruleReducedBy(token_list[i],sn) == rule) continue;
+
+      flag = token_list[i] == t;
+      if (flag) {
+	break;
+      }
+
+      kks = key_string(token_list[i]);
+      if (kks == NULL) {
+	continue;
+      }
+
+      //rts = map_key_word[map_token_number[token_list[i]].key].reserve;
+      //rts = Keyword(map_token_number[token_list[i]].key)->reserve;
+      rts = map_token_number[token_list[i]].key->reserve;
+
+      if (rt == rts) {
+	continue;
+      }
+      length = strlen((const char *) kks);
+      flag = length < strlen((const char *) ks) 
+	&& strncmp((const char *) ks,(const char *) kks, length) == 0;
+    }
+    if (!flag) {
+      continue;
+    }
+    rs = check_anomaly(sn,rule, rule->length(), keywordToken);
+    anomalyCases.reset();
+    if (rs) {
+      at(key_mess, sn, (int) keywordToken, (int) rule, rs);
+    }
+  }
+}
+
+void build_parse_table(void) {
+  unsigned n;
+  unsigned sn;
+  int default_off = !default_reductions || error_token != 0;
+
+  LOGSECTION("build_parse_table");
+  parse_table_length = 0;
+  max_pn = 0;
+  LOGV(nforms_base);
+  LOGV(nforms);
+  if (traditional_engine) nforms_base = nforms;
+  n = (n_gotos + n_completions + n_reductions +
+       n_default_reductions + nits);
+  check_size(a_actions_list,n,n);
+  check_size(t_actions_list,n,n);
+  check_size(p_actions_list,n,n);
+
+  LOGS("call build_key_tables");
+  LOGV(nforms_base);
+  LOGV(nforms);
+  build_key_tables();
+
+  nstates = nits;
+  LOGV(nstates);
+
+  for (sn = 0; sn < nstates; sn++) {
+    int default_ok = !default_off;
+    state_number_map *sp = &map_state_number[sn];
+    int error_flag = 0;
+    int error_act = pe_syn_error, error_pn = 0;
+    const int *items = dict_str(isht_dict, sn);
+    int null_token_flag = 0;
+    int null_token_form = 0;
+    unsigned tn, pn;
+    pe_parser_action act;
+    const unsigned *p;
+    unsigned i;
+
+    LOGV(sn);
+    LOGV(sp->n_completed_forms) LCV(sp->n_completions);
+    if (sp->n_completed_forms == 1) {
+      const unsigned *fp = lstptr(*sp, completed_forms);
+      Rule rule(*fp);
+      RuleDescriptor &ruleDescriptor(rule);
+/*
+      if (!default_ok && default_reductions) {
+        default_ok = !rule->proc_name || rule->immediate_proc;
+      }
+      default_ok &= rule->length() != 0;
+*/
+      if (!default_ok && default_reductions) {
+        default_ok = !ruleDescriptor.reductionRequired
+	  || ruleDescriptor.immediate_proc;
+        //default_ok = !ruleDescriptor.proc_name
+	//  || ruleDescriptor.immediate_proc;
+      }
+      default_ok &= ruleDescriptor.length() != 0;
+    }
+    default_ok &= !traditional_engine;
+    if (default_ok) {
+      int fn, fx;
+      items++;
+      fn = *items++;
+      fx = *items++;
+      if (fx > 1 && Rule(fn).token(fx-1)==Token(error_token)) {
+	default_ok=0;
+      }
+    }
+
+    iws();
+
+    /* first fast loops on terminal tokens */
+    n = find_completions(sn, &p);
+    for (i = 0; i < n; i++) {
+      Token token = *p++;
+      Rule rule = pn = *p++;
+      //assert(pn <= nforms_base);
+      assert((unsigned) rule <= nforms_base);
+      //if (tn == error_token) default_ok = 0;
+      if ((int) token == error_token) default_ok = 0;
+
+      //Rule rule(pn);
+      //Token token(tn);
+      if (token->non_terminal_flag) {
+	continue;
+      }
+      if (!rule->fast_loop) {
+	continue;
+      }
+      if (rule.isNull()) {
+	act = pe_shift_accept;
+      }
+      else if (!token->non_terminal_flag &&
+                rule->length() == 2 &&
+                ibnfn[(int) rule] == 1 &&
+                error_token != token &&
+                rule.token(0) == rule->prim_tkn)
+      {
+        act = pe_skip_reduce;
+        //if (rule->proc_name == 0) {
+        if (rule->reductionRequired == 0) {
+          act = pe_skip;
+        }
+      }
+      //else if (rule->proc_name || ibnfn[(int) rule] > 1)
+      else if (rule->reductionRequired || ibnfn[(int) rule] > 1) {
+        act = pe_shift_reduce;
+      }
+      else {
+	act = pe_shift_simple_reduce;
+      }
+      aws(act); aws(token); aws(rule);
+      LOGV(act) LCV(token) LCV(rule);
+    }
+    /* then completions by terminal tokens */
+    n = find_completions(sn, &p);
+    for (i = 0; i < n; i++) {
+      Token token = *p++;
+      Rule rule = pn = *p++;
+      assert( (unsigned) rule <= nforms_base);
+      if ((int) token == error_token) {
+	default_ok = 0;
+      }
+      if (token->non_terminal_flag) {
+	continue;
+      }
+      if (rule->fast_loop) {
+	continue;
+      }
+      if (rule.isNull()) {
+	act = pe_shift_accept;
+      }
+      //else if (rule->proc_name || ibnfn[(int)rule] > 1)
+      else if (rule->reductionRequired || ibnfn[(int)rule] > 1) {
+        act = pe_shift_reduce;
+      }
+      else {
+	act = pe_shift_simple_reduce;
+      }
+      if ((int) token == error_token) {
+        error_act = act;
+        error_pn = (int) rule;
+        error_flag = 1;
+        continue;
+      }
+      aws(act); aws((int) token); aws((int)rule);
+      LOGV(act) LCV((int) token) LCV((int) rule);
+    }
+    /* go_tos on terminal tokens first */
+    n = find_gotos(sn, &p);
+    for (i = 0; i < n; i++) {
+      tn = *p++; pn = *p++;
+      if (map_token_number[tn].non_terminal_flag) {
+	continue;
+      }
+      if ((int) tn == error_token) {
+        default_ok = 0;
+        error_act = pe_go_to;
+        error_pn = pn;
+        error_flag = 1;
+        continue;
+      }
+      aws(pe_go_to); aws(tn); aws(pn);
+      LOGV(pe_go_to) LCV(tn) LCV(pn);
+    }
+
+    /* now reductions */
+    LOGS("Now reductions") LCV(sp->n_reductions);
+    p = lstptr(*sp,reductions);
+    n = sp->n_reductions;
+    LOGV(default_ok) LCV(sp->n_completed_forms);
+    if (default_ok == 0 || sp->n_completed_forms != 1) {
+      for (i = 0; i < n; i++) {
+        tn = *p++; pn = *p++;
+        assert(pn <= nforms_base);
+        if (tn == 0) {
+          null_token_flag = 1;
+          null_token_form = pn;
+          continue;
+        }
+        act = pe_simple_reduce;
+        Rule rule(pn);
+        if (pn == 0) {
+	  act = pe_accept;
+	}
+        //else if (rule->proc_name || ibnfn[(int) rule] > 1)
+        else if (rule->reductionRequired || ibnfn[(int) rule] > 1) {
+          act = pe_reduce_form;
+	}
+        else if (rule->length() == 0
+                 && !traditional_engine
+                 && !rule_coverage
+                 && (int) tn != error_token) {
+          unsigned *p;
+          unsigned n,t;
+
+          p = lstptr(*sp, gotos);
+          n = sp->n_gotos;
+          t = rule->prim_tkn;
+          while (n && *p++ != t) {
+	    p++;
+	    n--;
+	  }
+          if (n && x2d(*p, tn)) {
+            LOGSECTION("pe_null_go_to");
+            pn = *p;
+            LOGV(sn) LCV(pn) LCV(tn) LCV(t) LCV(new_next_state(sn, tn));
+            act = pe_null_go_to;
+          }
+        }
+        aws(act); aws(tn); aws(pn);
+        LOGV(act) LCV(tn) LCV(pn);
+      }
+    }
+
+    if (error_flag) {
+      aws(error_act); aws(error_token); aws(error_pn);
+      LOGV(error_act) LCV(error_token) LCV(error_pn);
+    }
+    /* default action */
+    LOGS("default action");
+    if (null_token_flag) {
+      pn = null_token_form;
+      act = pe_simple_reduce;
+      if (pn == 0) {
+	act = pe_accept;
+      }
+      //else if (Rule(pn)->proc_name || ibnfn[pn] > 1)
+      else if (Rule(pn)->reductionRequired || ibnfn[pn] > 1) {
+        act = pe_reduce_form;
+      }
+    }
+    else if (default_ok &&
+      sp->n_completed_forms == 1) {
+      //form_number_map *fp;
+
+      p = lstptr(*sp,completed_forms);
+      pn = *p++;
+      LOGV(pn);
+
+      assert(pn <= nforms_base);
+      Rule rule(pn);
+      act = pe_simple_reduce;
+      if (pn == 0) {
+	act = pe_accept;
+      }
+      //else if (rule->proc_name || ibnfn[pn] > 1)
+      else if (rule->reductionRequired || ibnfn[pn] > 1) {
+        act = pe_reduce_form;
+      }
+      else if (sp->n_gotos == 0) {
+        act = pe_simple_reduce;
+      }
+    }
+    else {
+      act = pe_syn_error, pn = sn;
+    }
+
+    {
+      int *lb = list_base;
+      unsigned n = rws()/3;
+      int k = n;
+      tsd *tt = spec_tsd(n, 3);
+      sp->n_terminals = (int) n;
+      while (k--) {
+	int a = *lb++, t = *lb++, p = *lb++;
+	at(tt, t,a,p);
+      }
+      sort_tuples(tt, 1);
+      iws();
+      lb = tt->sb + 3*n;
+      while (n--) {
+	int p = *--lb, a = *--lb, t = *--lb;
+	aws(a), aws(t), aws(p);
+        LOGV(a) LCV(t) LCV(p);
+      }
+      delete_tsd(tt);
+    }
+
+    aws(act); aws(0); aws(pn);
+    LOGV(act) LCV(0) LCV(pn);
+    iws();
+    n = find_completions(sn, &p);
+    for (i = 0; i < n; i++) {
+      //token_number_map *tp;
+      //form_number_map *fp;
+
+      tn = *p++;
+      pn = *p++;
+
+      Token token(tn);
+      if (!token->non_terminal_flag) {
+	continue;
+      }
+      Rule rule(pn);
+      if (pn == 0) {
+	act = pe_shift_accept;
+      }
+      //else if (rule->proc_name || ibnfn[pn] > 1)
+      else if (rule->reductionRequired || ibnfn[pn] > 1) {
+        act = pe_shift_reduce;
+      }
+      else {
+	act = pe_shift_simple_reduce;
+      }
+      aws(act); aws(tn); aws(pn);
+      LOGV(act) LCV(tn) LCV(pn);
+    }
+    n = find_gotos(sn,&p);
+    for (i = 0; i < n; i++) {
+      tn = *p++;
+      pn = *p++;
+      if (!map_token_number[tn].non_terminal_flag) {
+	continue;
+      }
+      aws(pe_go_to); aws(tn); aws(pn);
+      LOGV(pe_go_to) LCV(tn) LCV(pn);
+    }
+
+
+    {
+      int *lb = list_base;
+      unsigned n = rws()/3;
+      int k = n;
+      tsd *tt = spec_tsd(n, 3);
+      while (k--) {
+	int a = *lb++, t = *lb++, p = *lb++;
+	at(tt, t,a,p);
+      }
+      sort_tuples(tt, 1);
+      iws();
+      lb = tt->sb;
+      while (n--) {
+	int t = *lb++, a = *lb++, p = *lb++;
+	aws(a), aws(t), aws(p);
+        LOGV(a) LCV(t) LCV(p);
+      }
+      delete_tsd(tt);
+    }
+    concat_list();
+    select_actions(0);
+    sp->a_actions_index = store_list(a_actions_list);
+    rws();
+    select_actions(1);
+    sp->t_actions_index = store_list(t_actions_list);
+    rws();
+    select_actions(2);
+    sp->p_actions_index = store_list(p_actions_list);
+    rws();
+    n = rws()/3;
+    parse_table_length += (sp->n_actions = n);
+
+    p = lstptr(*sp, p_actions);
+    for (i = 0; i < n; i++) max_pn = max(max_pn, p[i]);
+  }
+  LOGS("Ready to check keywords");
+  if (event_driven) parse_table_length++;
+  if (Keyword::count() > 1) {
+    if (badRecursionFlag) {
+      ssprintf("Keyword anomaly analysis aborted: %s", badRecursionFlag);
+      log_error();
+    }
+    else {
+      for (sn = 0; sn < nstates; sn++) {
+	check_keys(sn);
+      }
+    }
+  }
+}
+
+static void wr_char_map(const int *l) {
+  int i = min_char_number;
+  int k = 0;
+  unsigned n = *l++ - 1;
+
+  assert(n <= n_chars);
+  for (; n--; l++) {
+    assert(*l >= min_char_number && *l <= max_char_number);
+    for (; i <= *l; i++) {
+      wpe(" %d,", i == *l);
+      if (++k < 24) {
+	continue;
+      }
+      wpe("\n");
+      k = 0;
+    }
+  }
+  for (; i <= max_char_number; i++) {
+    wpe(" 0,");
+    if (++k < 24) {
+      continue;
+    }
+    wpe("\n");
+    k = 0;
+  }
+  if (k) {
+    wpe("\n");
+  }
+}
+
+static void write_key_tables(void) {
+  unsigned sn;
+  unsigned ps, *p;
+  int i;
+  unsigned ch, act, parm, jmp;
+  const char *cs;
+  const char *type = ntkns <= 255 ? "char" : ntkns <= 65535 ? "short" : "int";
+  unsigned ncs = char_set_dict->nsx;
+  int nkw = Keyword::count();
+  //int *map_cs_ax = local_array(ncs, int);
+  LocalArray<int> map_cs_ax(ncs);
+  //int *map_kw_tn = local_array(nkw, int);
+  LocalArray<int> map_kw_tn(nkw);
+  //unsigned char *cs_flag = local_array(ncs, unsigned char);
+  LocalArray<unsigned char> cs_flag(ncs);
+  int ncm;
+  int npt = 0;
+
+  memset(cs_flag, 0, ncs);
+  memset(map_kw_tn, 0, nkw * sizeof(int));
+
+  {
+    ncm = 0;
+    wpe("\nstatic %sunsigned char %sag_key_itt[] = {\n", constWord, LOCUS);
+    for (i = 1; i < nkw; i++) {
+      //unsigned cs = map_key_word[i].reserve;
+      Keyword keyword = i;
+      KeywordDescriptor &keywordDescriptor(keyword);
+      //int cs = keyword->reserve;
+      int cs = keywordDescriptor.reserve;
+      assert(cs < (int) ncs);
+      if (cs == 0) {
+	continue;
+      }
+      if (cs_flag[cs] == 0) {
+        map_cs_ax[cs] = ncm*n_chars - min_char_number;
+        ncm++;
+        cs_flag[cs] = ncm != 0;
+        wr_char_map(dict_str(char_set_dict, cs));
+      }
+      //map_kw_tn[i] = map_key_word[i].token_number;
+      //map_kw_tn[i] = keyword->token_number;
+      map_kw_tn[i] = keywordDescriptor.token_number;
+    }
+    wpe(" 0\n};\n\nstatic %sunsigned short %sag_key_pt[] = {\n",
+	constWord, LOCUS);
+    ps = 2;
+    cs = "";
+    for (i = 1; i < nkw; i++) {
+      Keyword keyword = i;
+      //int csn = map_key_word[i].reserve;
+      int csn = keyword->reserve;
+      if (csn == 0) {
+	continue;
+      }
+      ps += wpe("%s%3d,%3d", cs, map_cs_ax[csn], map_kw_tn[i]);
+      map_kw_tn[i] = npt;
+      npt += 2;
+      if (ps > 64) {
+	ps = 0, cs = ",\n";
+      }
+      else {
+	cs = ",";
+      }
+    }
+    wpe("%s0\n};\n",cs);
+  }
+  cs = "  ";
+  ps = 2;
+  p = (unsigned *) key_table->sb;
+  wpe("\nstatic %sunsigned char %sag_key_ch[] = {\n", constWord, LOCUS);
+  for (i = 0; i < (int) key_table->nt; i++) {
+
+    ch = *p++;
+    act = *p++;
+    parm = *p++;
+    jmp = *p++;
+
+    if (!case_sensitive) {
+      ch = agToUpper((char) ch);
+    }
+    ps += wpe("%s%3d", cs, ch);
+    if (ps > 72) {
+      ps = 0, cs  = ",\n  ";
+    }
+    else {
+      cs = ",";
+    }
+  }
+  wpe("\n};\n");
+
+  cs = "  ";
+  ps = 2;
+  p = (unsigned *) key_table->sb;
+  wpe("\nstatic %sunsigned char %sag_key_act[] = {\n", constWord, LOCUS);
+  for (i = 0; i < (int) key_table->nt; i++) {
+    //int key;
+
+    ch = *p++;
+    act = *p++;
+    parm = *p++;
+    jmp = *p++;
+
+    //key = map_token_number[parm].key;
+    Keyword key = map_token_number[parm].key;
+    //if (key && map_key_word[key].reserve) switch (act) {
+    if (key.isNotNull() && key->reserve) {
+      switch (act) {
+	case accept_key: act = cf_accept_key;
+	  break;
+	case set_key: act = cf_set_key;
+	  break;
+	case end_key: act = cf_end_key;
+	  break;
+      }
+    }
+    ps += wpe("%s%1d", cs, act);
+    if (ps > 72) {
+      ps = 0, cs  = ",\n  ";
+    }
+    else {
+      cs = ",";
+    }
+  }
+  wpe("\n};\n");
+
+  cs = "  ";
+  ps = 2;
+  p = (unsigned *) key_table->sb;
+
+  wpe("\nstatic %sunsigned %s %sag_key_parm[] = {\n",constWord, type, LOCUS);
+  for (i = 0; i < (int) key_table->nt; i++) {
+    //int key;
+
+    ch = *p++;
+    act = *p++;
+    parm = *p++;
+    jmp = *p++;
+
+    //key = map_token_number[parm].key;
+    Keyword key = map_token_number[parm].key;
+    //if (key && map_key_word[key].reserve) switch (act) {
+    if (key.isNotNull() && key->reserve) {
+      switch (act) {
+	case accept_key:
+	case set_key:
+	case end_key:
+	  parm = map_kw_tn[(int) key];
+      }
+    }
+    ps += wpe("%s%3d", cs, parm);
+    if (ps > 72) {
+      ps = 0, cs  = ",\n  ";
+    }
+    else {
+      cs = ",";
+    }
+  }
+  wpe("\n};\n");
+
+  cs = "  ";
+  ps = 2;
+  p = (unsigned *) key_table->sb;
+
+  /*
+   * Originally this was (equivalent to)
+   *
+   *    size = key_table->nt
+   *
+   * but that was commented out and replaced with (code equivalent to)
+   *
+   *    size = n_key_ends;
+   *
+   * but that definitely breaks if there are more than 256 keywords;
+   * see for example manykw.syn in the test suite. It's also clear
+   * from the parse engine code that there can be cases where
+   * n_key_ends matters. So take the max. I think token numbers
+   * (where one would check ntkns as above) are not involved here.
+   *
+   * - dholland 6/3/2007
+   */
+
+  unsigned size = max(key_table->nt, n_key_ends);
+  type = size <= 255 ? "char"
+       : size <= 65535 ? "short"
+       : "int";
+
+  wpe("\nstatic %sunsigned %s %sag_key_jmp[] = {\n", constWord, type, LOCUS);
+  for (i = 0; i < (int) key_table->nt; i++) {
+
+    ch = *p++;
+    act = *p++;
+    parm = *p++;
+    jmp = *p++;
+
+    ps += wpe("%s%3d", cs, jmp);
+    if (ps > 72) {
+      ps = 0, cs  = ",\n  ";
+    }
+    else {
+      cs = ",";
+    }
+  }
+  wpe("\n};\n");
+
+  cs = "  ";
+  ps = 2;
+
+  if (key_table->nt > 65535) {
+    type = "int";
+  }
+  else if (key_table->nt >255) {
+    type = "short";
+  }
+  else {
+    type = "char";
+  }
+
+  wpe("\nstatic %sunsigned %s %sag_key_index[] = {\n", constWord, type, LOCUS);
+  for (sn = 0; sn < nstates; sn++) {
+    ps += wpe("%s%3d", cs, map_state_number[sn].key_index);
+    if (ps > 72) {
+      ps = 0, cs  = ",\n  ";
+    }
+    else {
+      cs = ",";
+    }
+  }
+  if (event_driven) {
+    wpe("%s%3d", cs, 0);
+  }
+  wpe("\n};\n");
+
+  ps = 2;
+  wpe("\nstatic %sunsigned char %sag_key_ends[] = {\n", constWord, LOCUS);
+  if (n_key_ends) {
+    for (i = 0; i < (int) n_key_ends; ) {
+      char *s = key_ends+i;
+      int k = 3*strlen(s) + 4;
+      cs = "";
+      if (ps + k> 72) {
+	cs = "\n", ps = 0;
+      }
+      sss(cs);
+      if (case_sensitive) {
+	while (*s) {
+	  apprintf("%u,", (unsigned char) *s++);
+	}
+      }
+      else {
+	while (*s) {
+	  apprintf("%u,", (unsigned char) agToUpper(*s++));
+	}
+      }
+      ass("0, ");
+      acs(0);
+      ps += wss();
+      i += strlen(key_ends+i)+1;
+    }
+  }
+  else {
+    wpe("  0");
+  }
+  wpe("\n};\n");
+}
+
+static const char *more = ",";
+static int lm = 1;
+static const char *line = ",\n  ";
+
+static void write_parse_table_actions (void) {
+  const char *cs = "  ";
+  unsigned ks;
+  unsigned sx = 0;
+  int kw = 0;
+
+  wpe("\nstatic unsigned %schar %sag_astt[%d] = {\n",
+      constWord, LOCUS, parse_table_length);
+
+  for (ks = 0; ks < nstates; ks++) {
+    state_number_map *sp = &map_state_number[ks];
+    unsigned *p = lstptr(*sp, a_actions);
+    int n = sp->n_actions;
+    int i;
+
+    for (i = 0; i < n; i++) {
+      unsigned an = *p++;
+      int nc = ssprintf("%u", an);
+      if (an > pe_recover) {
+	assert(0);
+      }
+      sx++;
+      if (kw + nc >= parserLineLength) {
+        cs = line;
+        kw = -2;
+      }
+      wps(cs);
+      kw += strlen(cs) + wss();
+      cs = more;
+    }
+    sp->engine_index = sx - 1;
+  }
+  if (event_driven) {
+    wpe("%s%d\n};\n\n", cs, pe_recover);
+    max_engine_index = sx;
+    return;
+  }
+  wpe("\n};\n\n");
+  max_engine_index = sx -1;
+}
+
+static void write_parse_table_params (void) {
+  const char *cs = "";
+  unsigned ks;
+  unsigned sx = 0;
+  int lc = 0;
+  unsigned np = max_pn;
+  const char *type = np <= 255 ? "char" : np <= 65535 ? "short" : "int";
+
+  wpe("\nstatic %sunsigned %s %sag_pstt[] = {\n", constWord, type, LOCUS);
+  for (ks = 0; ks < nstates; ks++) {
+    state_number_map *sp = &map_state_number[ks];
+    int kw = 0;
+    unsigned *p = lstptr(*sp, p_actions);
+    int n = sp->n_actions;
+    int i;
+
+    for (i = 0; i < n; i++) {
+      int nc = ssprintf("%u", *p++);
+      sx++;
+      if (kw + nc + lc >= parserLineLength) {
+        cs = line;
+        kw = 0;
+      }
+      wps(cs);
+      kw += strlen(cs) + wss();
+      cs = more;
+      lc = lm;
+    }
+    sp->engine_index = sx - 1;
+    wps(",\n");
+    cs = "";
+    lc = 0;
+  }
+  if (event_driven) {
+    wpe("  0\n};\n\n");
+    max_engine_index = sx;
+    return;
+  }
+  wpe("\n};\n\n");
+  max_engine_index = sx -1;
+}
+
+static void set_my_macro(const char *, const char *, ...) PRINTFFY (2,3);
+
+static void write_parse_table_tokens (void) {
+  const char *cs = "";
+  unsigned ks;
+  unsigned sx = 0;
+  int lc = 0;
+  const char *type = ntkns <= 255 ? "char" : ntkns <= 65535 ? "short" : "int";
+
+  set_my_macro("AG_TSTT_TYPE","%sunsigned %s", constWord, type);
+  set_my_macro("AG_TSTT_CAST","unsigned %s", type);
+  wpe("static %sunsigned %s %sag_tstt[] = {\n", constWord, type, LOCUS);
+
+  for (ks = 0; ks < nstates; ks++) {
+    state_number_map *sp = &map_state_number[ks];
+    //int nterm = 0;
+    int kw = 0;
+    unsigned *p = lstptr(*sp, t_actions);
+    int n = sp->n_actions;
+    int i;
+
+    for (i = 0; i < n; i++) {
+      int nc;
+      int tn = *p++;
+
+      //if (tn && !map_token_number[tn].non_terminal_flag) nterm++;
+      nc = ssprintf("%u",tn);
+      sx++;
+      if (kw + nc + lc >= parserLineLength) {
+        cs = line;
+        kw = 0;
+      }
+      wps(cs);
+      kw += strlen(cs) + wss();
+      cs = more;
+      lc = lm;
+    }
+    sp->engine_index = sx - 1;
+    wps(",\n");
+    cs = "";
+    lc = 0;
+  }
+  if (event_driven) {
+    wpe("  0\n};\n\n");
+    max_engine_index = sx;
+    return;
+  }
+  max_engine_index = sx -1;
+  wpe("\n};\n\n");
+}
+
+static void write_header(const char *pname) {
+  if (rule_coverage) {
+    AgString cfile = subs_template(simple_file_name.pointer(), 
+				   coverage_file_name.pointer(), '#');
+
+    set_my_macro("AG_COUNT_FILE", "\"%s\"", cfile.pointer());
+    set_my_macro("AG_COUNT_FILE_ID", "%ldL", (long) infile_time_stamp);
+    set_my_macro("AG_READ_COUNTS", "%s%s_read_counts(%s)",
+		 VOIDB, pname, voidWord);
+    set_my_macro("AG_WRITE_COUNTS", "%s%s_write_counts(%s)",
+		 VOIDB, pname, voidWord);
+    define_macro("READ_COUNTS", "%s_read_counts()", pname);
+    define_macro("WRITE_COUNTS", "%s_write_counts()", pname);
+    set_my_macro("AG_COUNT_FILE_RC", "%d", nforms_base+1);
+    set_my_macro("AG_RULE_COUNT", "%s_nrc", pname);
+
+    set_my_macro("AG_COUNT_RULE_P", "%s_nrc[(PCB).ag_ap]++;", pname);
+    set_my_macro("AG_COUNT_RULE_Z", "%s_nrc[0]++;", pname);
+
+    wpe("unsigned %s_nrc[%d];\n", pname, nforms_base+1);
+    write_code_segment("read write counts");
+    //DEALLOCATE(cfile);
+  }
+  else {
+    set_my_macro("AG_COUNT_RULE_P", "%s", "");
+    set_my_macro("AG_COUNT_RULE_Z", "%s", "");
+    define_macro("READ_COUNTS", "%s", "");
+    define_macro("WRITE_COUNTS", "%s", "");
+  }
+}
+
+static void writePrologue() {
+  LOGSECTION("writePrologue");
+  LOGV(nPrologueSegments);
+  if (nPrologueSegments == 0) {
+    return;
+  }
+  nPrologueSegments = 1;
+  CSegment &segment = cSegmentStack[0];
+  if (segment.length()
+      && fwrite(segment.begin, segment.length(), 1, pe_file) == 0) {
+    parserWriteError = EOF;
+  }
+  count_pe_line((const char *)segment.begin, segment.length());
+}
+
+static void writeEmbeddedC() {
+  LOGSECTION("writeEmbeddedC");
+  int n = cSegmentStack.size();
+  int i = nPrologueSegments;
+  while (i < n) {
+    CSegment &segment = cSegmentStack[i];
+    if (line_numbers) {
+      wpe("#line %d \"%s\"\n", segment.line, ln_file_name.pointer());
+    }
+    else {
+      wpe("/*  Line %d, %s */\n", segment.line, ln_file_name.pointer());
+    }
+    if (segment.length() 
+	&& fwrite(segment.begin, segment.length(),1,pe_file) == 0) {
+      parserWriteError = EOF;
+    }
+    count_pe_line((const char *) segment.begin, segment.length());
+    if (line_numbers) {
+      wpe("#line %d \"%s\"\n", pe_line_count+2, ln_pe_name.pointer());
+    }
+    i++;
+  }
+  if ((unsigned) nPrologueSegments < cSegmentStack.size()) {
+    wps("\n");
+  }
+}
+
+static void writePCBExtensions() {
+  LOGSECTION("writePCBExtensions");
+  int n = extensionStack.size();
+  int i = 0;
+  while (i < n) {
+    CSegment &segment = extensionStack[i];
+    headerWriteError = fprintf(h_file, "/*  Line %d, %s */\n",
+			       segment.line, ln_file_name.pointer());
+    if (fwrite(segment.begin, segment.length(),1,h_file) == 0) {
+      headerWriteError = 1;
+    }
+    i++;
+  }
+}
+
+static void define_proc(Procedure proc, int ilFlag, AgString thisArg,
+			AgString thisArgc) {
+  LOGSECTION("define_proc");
+  const char *cs = thisArgc.pointer();
+  const char *ft = proc->cast == 1 ? voidWord : Cast(proc->cast)->pointer();
+  const char *ilString = ilFlag ? "inline " : "";
+  AgString ns = proc_name_string(proc);
+  LOGV(proc) LCV(cs) LCV(ft) LCV(proc->cast) LCV(ns);
+  wpe("\nstatic %s%s %s%s(", ilString, ft, nearWord, ns.pointer());
+
+  //AgArray<RuleElement> elementList = Rule(proc->form_number)->elementList;
+  Rule rule(proc->form_number);
+  int bias = rule->op_bias;
+  LOGV(rule) LCV(bias);
+  AgArray<RuleElement> elementList = 
+    bias ? rule->hostElementList : rule->elementList;
+
+  if (really_old_style) {
+    unsigned j;
+    for (j = 0; j < elementList.size(); j++) {
+      RuleElement &element = elementList[j];
+      if (element.cVariable == 0) {
+	continue;
+      }
+      wpe("%s%s",cs, cVariableList[element.cVariable].pointer());
+      cs = ", ";
+    }
+    wpe(")\n");
+    for (j = 0; j < elementList.size(); j++) {
+      RuleElement &element = elementList[j];
+      if (element.cVariable == 0) {
+	continue;
+      }
+      wpe("  %s %s;\n",
+	  Cast(element.token->value_type)->pointer(),
+	  cVariableList[element.cVariable].pointer());
+          //dict_str(cast_dict, element.token->value_type),
+          //element.cVariable->pointer());
+    }
+    wpe("{\n");
+  }
+  else {
+    unsigned j;
+    int argCount = 0;
+    for (j = 0; j < elementList.size(); j++) {
+      RuleElement &element = elementList[j];
+      if (element.cVariable == 0) {
+	continue;
+      }
+      const char *reference = "";
+      Cast type = element.token->value_type;
+      if (type.wrapperRequired()) {
+	reference = "&";
+      }
+
+      //wpe("%s%s %s", cs, 
+      //  dict_str(cast_dict, element.token->value_type),
+      //  element.cVariable->pointer());
+
+      wpe("%s%s %s%s", cs,
+	  Cast(element.token->value_type)->pointer(), reference,
+	  cVariableList[element.cVariable].pointer());
+
+      argCount++;
+      cs = ", ";
+    }
+    //cs = argCount ? "" : voidWord;
+    cs = argCount ? "" : thisArg.pointer();
+    LOGV(argCount) LCV(cs);
+    wpe("%s) {\n",cs);
+  }
+}
+
+static void print_token_names(const char *pname) {
+  LOGSECTION("print_token_names");
+
+  define_macro("TOKEN_NAMES", "%s_token_names", pname);
+  wpe("%schar *%s%s_token_names[%d] = {\n  \"%s\",\n",
+      constWord, constWord, pname, ntkns+1,
+      Token(grammar_token)->token_name->string.pointer());
+
+  for (Each<Token> token; token.loopNotFinished(); token.getNext()) {
+    char *string;
+    Symbol name = token->token_name;
+
+    ics();
+    LOGV(token_names_only) LCV(token->fine_structure) LCV(name.isNotNull());
+    if (token->fine_structure) {
+      /* nothing */
+    }
+    else if (name.isNotNull()) {
+      ass(name->string.pointer());
+    }
+    else if (!token_names_only) {
+      Keyword key = token->key;
+      ParseTree tree = token->parse_tree;
+      LOGV(key.isNotNull()) LCV(tree.isNotNull());
+      if (tree.isNotNull()) {
+	LOGV(tree->expression->type);
+      }
+      if (key.isNotNull()) {
+        LOGV(key);
+	acs('"');
+	append_key(key);
+	acs('"');
+      }
+      else if (tree.isNotNull()
+	       && tree->expression->type == CharSetExpression::individualChar){
+	IndividualChar *expression = (IndividualChar *) (tree->expression);
+        LOGV(expression->asString().pointer());
+	//acs(expression->asciiValue);
+	ass(expression->asString().pointer());
+      }
+    }
+    acs(0);
+    LOGV(string_base);
+    string_space(2*tis());
+    string = string_base;
+    sss("  \"");
+    while (*string) {
+      append_string_char(*string++);
+    }
+    ass("\",\n");
+    wss();
+    rcs();
+  }
+  wpe("\n};\n\n");
+}
+
+
+#define N_MY_MACROS 150
+
+char *my_macros_subs[N_MY_MACROS];
+
+static void set_my_macro(const char *m, const char *s, ...) {
+  LOGSECTION("set_my_macro");
+  LOGV(m) LCV(s);
+  int k = add_string_dict(m, my_macros);
+  va_list ap;
+  char buf[500];
+  int n;
+
+  va_start(ap,s);
+  n = vsprintf(buf, s, ap);
+  assert (n < 500);
+  assert(k < N_MY_MACROS);
+  if (my_macros_subs[k]) {
+    DEALLOCATE(my_macros_subs[k]);
+  }
+  my_macros_subs[k] = mystrdup(buf);
+  va_end(ap);
+}
+
+static void rename_macro(const char *sword, const char *sval) {
+  LOGSECTION("rename_macro");
+  LOGV(sword) LCV(sval);
+  char *word;
+  char *val;
+  int index;
+
+  const char *s = sword;
+  ics();
+  while (*s && (isalpha(*s) || *s == '_')) {
+    acs(*s++);
+  }
+  word = build_string();
+
+  ics();
+  s = sval;
+  while (*s && (isalpha(*s) || *s == '_')) {
+    acs(*s++);
+  }
+  val = build_string();
+
+  index = add_string_dict(word, my_macros);
+  assert(index < N_MY_MACROS);
+  if (my_macros_subs[index]) {
+    DEALLOCATE(my_macros_subs[index]);
+  }
+  my_macros_subs[index] = val;
+  DEALLOCATE(word);
+}
+
+static void define_my_macros(const char *seg) {
+  AgString text = code_segment(seg);
+  if (!text.exists()) {
+    return;
+  }
+  char *buf = text.pointer();
+  char *s = buf;
+  char *val;
+  char *word;
+  int index;
+
+  while (*s) {
+    while (*s && *s == ' ') {
+      s++;
+    }
+    if (*s == '\n') {
+      s++;
+      continue;
+    }
+
+    ics();
+    while (*s && (isalpha(*s) || *s == '_')) {
+      acs(*s++);
+    }
+    ics();
+    while (*s) {
+      while (*s && *s == ' ') {
+	s++;
+      }
+      if (strncmp(s,"\\\n", 2)) {
+	break;
+      }
+      s += 2;
+    }
+    while (*s && *s != '\n') {
+      if (*s == '\\') {
+	s++;
+      }
+      acs(*s++);
+    }
+    if (*s == '\n') {
+      s++;
+    }
+    val = build_string();
+    word = build_string();
+    index = add_string_dict(word, my_macros);
+    assert(index < N_MY_MACROS);
+    if (my_macros_subs[index]) {
+      DEALLOCATE(my_macros_subs[index]);
+    }
+    my_macros_subs[index] = val;
+    DEALLOCATE(word);
+  }
+}
+
+static int argsCompare(AgArray<RuleElement> x, AgArray<RuleElement> y) {
+  unsigned kx = 0, ky = 0;
+  while (kx < x.size() && ky < y.size()) {
+    while (kx < x.size() && x[kx].cVariable == 0) {
+      kx++;
+    }
+    while (ky < y.size() && y[ky].cVariable == 0) {
+      ky++;
+    }
+    if (kx == x.size() && ky == y.size()) {
+      return 1;
+    }
+    if (kx == x.size() || ky == y.size()) {
+      return 0;
+    }
+    if (x[kx].cVariable != y[ky].cVariable) {
+      return 0;
+    }
+    if (x[kx].token->value_type != y[ky].token->value_type) {
+      return 0;
+    }
+    kx++;
+    ky++;
+  }
+  return 0;
+}
+
+static int aliasReductionProc(Procedure proc) {
+  LOGSECTION("aliasReductionProc");
+  LOGV(proc);
+  Procedure aliasProc = 1;
+  while ((int)aliasProc < (int)proc) {
+    if (aliasProc->cSegment.length() != proc->cSegment.length()) {
+      return 0;
+    }
+    if (aliasProc->cast != proc->cast) {
+      return 0;
+    }
+    if (strncmp((const char *)aliasProc->cSegment.begin,
+		(const char *) proc->cSegment.begin,
+		proc->cSegment.length())) {
+      return 0;
+    }
+    if (argsCompare(Rule(aliasProc->form_number)->elementList,
+		    Rule(proc->form_number)->elementList)) {
+      return aliasProc;
+    }
+    aliasProc = (int) aliasProc + 1;
+  }
+  return 0;
+}
+
+
+void build_parse_engine(void) {
+  LOGSECTION("build_parse_engine");
+  unsigned i;
+  unsigned j;  //,k;
+  unsigned n;
+  int kw;
+  int tn;
+  const char *cs, *vs;
+  const char *cp;
+  const char *type;
+  really_old_style = old_style && !reentrant_parser;
+  AgString pname = subs_template(simple_file_name.pointer(),
+				 parser_name.pointer(), '#');
+  AgString cbt = subs_template(pname.pointer(), control_block_type, '$');
+  AgString structName = subs_template(pname.pointer(), structNameTemplate,'%');
+  AgString thisArg(reentrant_parser ? "PCB_DECL" : "void");
+  AgString thisArgc(reentrant_parser ? "PCB_DECL, " : "");
+  AgString pcbArg = reentrant_parser ?  "PCB_POINTER" : "";
+  AgString pcbArgc = reentrant_parser ?  "PCB_POINTER, " : "";
+  AgString vst = subs_template(pname.pointer(), value_stack_type, '$');
+  AgString token_type = subs_template(pname.pointer(), typedef_name, '$');
+  AgString h_file_name = subs_template(simple_file_name.pointer(),
+				       header_file_name.pointer(), '#');
+  AgString itt = subs_template(pname.pointer(), input_type_name, '$');
+  AgString enum_name = subs_template(pname.pointer(),
+				     enum_constant_name.pointer(), '$');
+  const char *void_string = really_old_style ? "" : "void";
+  const char *stack_value;
+  const char *stack_result;
+  const char *stack_location;
+  int bt = backtrack || auto_resynch || error_token;
+  int *rtkn_list = NULL;
+  int *rtkn_ndx = 0;
+
+  static unsigned rtkn_count = 0;
+
+  const char *csp;
+  unsigned buildTime = (unsigned) time(0);
+
+  constWord = "const ";
+  nearWord  = "near ";
+  LOCUS = "";
+  VOIDB  = "void ";
+  voidWord   = "void";
+
+  Cast defaultType(default_token_type);
+
+  if (line_numbers_path.exists()) {
+    ln_file_name = line_numbers_path;
+  }
+  else if (escape_backslashes) {
+    int length = infile_name.size();
+    int k = length;
+    int n = 0;
+    while (k--) {
+      if (infile_name.operator[](k) == '\\') {
+	n++;
+      }
+    }
+    AgCharStack temp;
+    char *q = infile_name.pointer();
+    while (*q) {
+      temp.push(*q);
+      if (*q == '\\') {
+	temp.push('\\');
+      }
+      q++;
+    }
+    ln_file_name = temp.popString();
+  }
+  else {
+    ln_file_name = infile_name.pointer();
+    int length = ln_file_name.size();
+    int k = length;
+    while (k--) {
+      if (ln_file_name[k] == '\\') {
+	ln_file_name[k] = '/';
+      }
+    }
+  }
+  LOGV(ln_file_name.pointer());
+  my_macros = null_str_dict();
+  for (i = 0; i < rename_macro_list->nt; i++) {
+    unsigned old_name, new_name;
+    xtx(rename_macro_list, i, &old_name, &new_name);
+    rename_macro((const char *)(input_base+old_name),
+		 (const char *)(input_base+new_name));
+  }
+
+  if (error_token) {
+    auto_resynch = 0;
+  }
+
+  pe_file_name = subs_template(simple_file_name.pointer(),
+			       parser_file_name.pointer(), '#');
+  if (pe_file_name[0] == '.'&& pe_file_name[1]!='.') {
+    AgString pattern = pe_file_name.pointer();
+    pattern[0] = '#';
+    pe_file_name = subs_template(work_dir_name.pointer(),
+				 pattern.pointer(), '#');
+  }
+  LOGV(pe_file_name);
+  if (escape_backslashes) {
+    //int length = strlen(pe_file_name);
+    int length = pe_file_name.size();
+    int k = length;
+    int n = 0;
+    while (k--) {
+      if (pe_file_name[k] == '\\') {
+	n++;
+      }
+    }
+    ln_pe_name = AgString(length + n);
+    LOGV(length) LCV(n);
+    char *q = ln_pe_name.pointer();
+    for (k = n = 0; k < length; k++) {
+      q[n++] = pe_file_name[k];
+      LOGV(k) LCV(n);
+      if (pe_file_name[k] == '\\') {
+	q[n++] = '\\';
+      }
+    }
+  }
+  else {
+    ln_pe_name = pe_file_name.pointer();
+    int k = ln_pe_name.size();
+    while (k--) {
+      if (ln_pe_name[k] == '\\') {
+	ln_pe_name[k] = '/';
+      }
+    }
+  }
+  LOGV(ln_pe_name);
+  LOGV(pe_file_name);
+  LOGV(h_file_name);
+  const char *openMode = no_cr ? "wb" : "w";
+  pe_file = fopen(pe_file_name.pointer(), openMode);
+  parserWriteError = 0;
+  pe_line_count = 0;
+  buildErrorMsg[0] = 0;
+  if (pe_file == NULL) {
+    sprintf(buildErrorMsg, "Cannot open %s", pe_file_name.pointer());
+    //DEALLOCATE(pe_file_name);
+    return;
+  }
+  h_file = fopen(h_file_name.pointer(),openMode);
+  headerWriteError = 0;
+  if (h_file == NULL) {
+    sprintf(buildErrorMsg, "Cannot open %s", h_file_name.pointer());
+    //DEALLOCATE(h_file_name);
+    fclose(pe_file);
+    return;
+  }
+
+  grammar_type = map_token_number[grammar_token].value_type;
+  if (grammar_type.isNull()) {
+    grammar_type = default_token_type;
+  }
+  sfn = simple_file_name.pointer();
+  LOGV(sfn);
+  sfn.toUpper();
+  LOGV(sfn);
+  headerWriteError = fprintf(h_file,
+    "#ifndef %s_H_%u\n"
+    "#define %s_H_%u\n\n",
+    sfn.pointer(), buildTime, sfn.pointer(), buildTime);
+
+  if (Cast::nWrappers) {
+    AgString def = code_segment("wrapper def");
+    headerWriteError = fprintf(h_file, "%s\n", def.pointer());
+  }
+
+  // Make list of terminal tokens
+  AgBalancedTree<int> typeList;
+  Each<Token> token;
+  for (token.restart(); token.loopNotFinished(); token.getNext()) {
+    if (token->non_terminal_flag) {
+      continue;
+    }
+    if (token->value_type > 1) {
+      typeList.insert(token->value_type);
+    }
+  }
+  n = typeList.size();
+  LOGV(typeList.size());
+  if (n <= 1) {
+    input_token_type = n ? typeList[0] : default_input_type;
+    itt = Cast(input_token_type).name();
+  }
+  else {
+    unsigned k;
+
+    headerWriteError = fprintf(h_file, "typedef union {\n");
+    for (k = 0; k < n; k++) {
+      int i = typeList[k];
+      if (i == void_token_type) continue;
+      LOGV(i) LCV(Cast(i)->pointer());
+      Cast type = i;
+      if (type.wrapperRequired()) {
+        headerWriteError = 
+	  fprintf(h_file, "  char ag_vt_%d[sizeof(AgObjectWrapper<%s >)];\n",
+		  i, type->pointer());
+      }
+      else {
+        headerWriteError = 
+	  fprintf(h_file, "  char ag_vt_%d[sizeof(%s)];\n",
+		  i, type->pointer());
+      }
+    }
+    LOGV(itt);
+    headerWriteError = fprintf(h_file, "} %s;\n\n", itt.pointer());
+    input_token_type = Cast(itt.pointer());
+  }
+
+  typeList.reset();
+  LOGV(Cast::count());
+  for (token.restart(); token.loopNotFinished(); token.getNext()) {
+    //int tt = map_token_number[i].value_type;
+    Cast type = token->value_type;
+    if ((int) type == void_token_type) {
+      continue;
+    }
+    LOGV(type);
+    LOGV(type->pointer());
+    if ((int) type == 0 || typeList.insert((int) type)) {
+      continue;
+    }
+    if (strcmp(type->pointer(), "double")) {
+      continue;
+    }
+    if (parser_stack_alignment == long_token_type) {
+      parser_stack_alignment = (int) type;
+    }
+  }
+  n = typeList.size();
+  LOGV(typeList.size());
+  if (typeList.size() == 0) {
+    typeList.insert(int_token_type);
+  }
+  n = typeList.size();
+  if (n == 1) {
+    vst = Cast(typeList[0]).name();
+    stack_value = "VS(%d)";
+    stack_result = "VS(0) = ";
+    stack_location = "(PCB).vs[(PCB).ssx]";
+  }
+  else {
+    stack_value = "V(%d,(%s *))";
+    stack_result = "V(0,(%s *)) = ";
+    stack_location = "(*(%s *) &(PCB).vs[(PCB).ssx])";
+    headerWriteError = fprintf(h_file, "typedef union {\n");
+    if (parser_stack_alignment != void_token_type)  {
+      LOGV(parser_stack_alignment);
+      LOGV(Cast(parser_stack_alignment)->pointer());
+      headerWriteError = fprintf(h_file, "  %s alignment;\n",
+				 //dict_str(cast_dict, parser_stack_alignment)
+				 Cast(parser_stack_alignment)->pointer());
+    }
+    for (i = 0; i < n; i++) {
+      //int j = list_base[i];
+      int j = typeList[i];
+      if (j == void_token_type) {
+	continue;
+      }
+      LOGV(j) LCV(Cast(j)->pointer());
+      Cast type = j;
+      if (type.wrapperRequired()) {
+        headerWriteError = 
+	  fprintf(h_file, "  char ag_vt_%d[sizeof(AgObjectWrapper<%s >)];\n",
+		  j, type->pointer());
+      }
+      else {
+        headerWriteError = 
+	  fprintf(h_file, "  char ag_vt_%d[sizeof(%s)];\n",
+		  j, type->pointer());
+      }
+    }
+    headerWriteError = fprintf(h_file, "} %s;\n\n", vst.pointer());
+  }
+
+  LOGS("Ready to write prologue");
+  writePrologue();
+  LOGS("Prologue written");
+  wpe("\n");
+
+  LOGV(copyrightMessage);
+  wpe(code_segment(copyrightMessage).pointer(), 
+      VERSIONSTRING, build_date.pointer());
+
+  set_my_macro("STATIC", "static ");
+  set_my_macro("QUAL", "%s", "");
+  if (!near_functions) {
+    nearWord = "";
+  }
+  cp = near_functions ? "near " : "";
+  set_my_macro("NEAR", cp);
+  cp = far_tables ? "far" : near_functions ? "near" : "";
+  if (far_tables) {
+    LOCUS = "far ";
+  }
+  set_my_macro("LOCUS", LOCUS);
+  cp = really_old_style ? "" : "void";
+  if (really_old_style) {
+    voidWord = VOIDB = "";
+  }
+  set_my_macro("VOID", cp);
+  set_my_macro("THISARG", thisArg.pointer());
+  set_my_macro("THISARGC", thisArgc.pointer());
+  set_my_macro("PCBARG", pcbArg.pointer());
+  set_my_macro("PCBARGC", pcbArgc.pointer());
+  if (really_old_style || !const_data) {
+    constWord = "";
+  }
+  set_my_macro("CONST", constWord);
+
+  set_my_macro("MISSING", "%s", missing_diagnostic);
+  set_my_macro("UNEXPECTED", "%s", unexpected_diagnostic);
+
+  wpe("\n#ifndef %s_H_%u\n"
+      "#include \"%s\"\n"
+      "#endif\n\n"
+      "#ifndef %s_H_%u\n"
+      "#error Mismatched header file\n"
+      "#endif\n\n",
+      sfn.pointer(), buildTime, h_file_name.pointer(),
+      sfn.pointer(), buildTime);
+  if (diagnose_errors) {
+    wpe("#include <ctype.h>\n");
+  }
+  write_code_segment("header");
+  if (reentrant_parser) {
+    wpe("#ifndef PCB_TYPE\n#define PCB_TYPE %s\n#endif\n\n", cbt.pointer());
+    wpe("\n#define PCB (*pcb_pointer)\n");
+    set_my_macro("PCB_TYPE_CAST", "(PCB_TYPE *)");
+    wpe("#define PCB_DECL PCB_TYPE *pcb_pointer\n");
+    wpe("#define PCB_POINTER pcb_pointer\n");
+  }
+  else {
+    set_my_macro("PCB_TYPE_CAST", "%s", "");
+    if (declare_pcb) {
+      wpe("\n%s %s_pcb;\n#define PCB %s_pcb\n",
+	  cbt.pointer(), pname.pointer(), pname.pointer());
+    }
+  }
+  rtkn_count = 0;
+  if (semantic_productions) {
+    const char *as = really_old_style ? "" : token_type.pointer();
+    const char *ds = really_old_style ? "int ag_k;" : "";
+    AgString enum_temp = subs_template("##x##", enum_name.pointer(), '%');
+    unsigned fn, nsd=0;
+    define_macro("CHANGE_REDUCTION(x)",
+		 "%s_change_reduction(%s%s)",
+		 pname.pointer(), pcbArgc.pointer(), enum_temp.pointer());
+    set_my_macro("AG_CHANGE_REDUCTION",
+		 "%s_change_reduction(%s%s ag_k)%s",
+		 pname.pointer(), thisArgc.pointer(), as,ds);
+    wpe("int %s_change_reduction(%s%s);\n\n",
+	pname.pointer(), thisArgc.pointer(), as);
+
+    as = really_old_style ? "ag_tp" : "int *ag_tp";
+    ds = really_old_style ? "int *ag_tp" : "";
+    if (reduction_choices) {
+      set_my_macro("AG_CHOICES",
+		   "%s_reduction_choices(%s%s)%s",
+		   pname.pointer(), thisArgc.pointer(), as, ds);
+      define_macro("REDUCTION_CHOICES(x)",
+		   "%s_reduction_choices(%sx)",
+		   pname.pointer(), pcbArgc.pointer());
+      wpe("int %s_reduction_choices(%s%s);\n",
+	  pname.pointer(), thisArgc.pointer(), as);
+    }
+    for (fn = 0; fn++ < nforms_base;) {
+      if (ibnfn[fn] > 1) {
+	nsd += ibnfn[fn] + 1;
+      }
+    }
+    rtkn_count = nsd;
+    rtkn_list = local_array(nsd, int);
+    rtkn_ndx = local_array(nforms_base+1, int);
+    nsd = 0;
+    for (fn = 0; fn++ < nforms_base;) {
+      int n = ibnfn[fn];
+      const int *p;
+      if (n == 1) {
+	continue;
+      }
+      p = ibnfs+ibnfb[fn];
+      rtkn_ndx[fn] = nsd;
+      while (n--) {
+	rtkn_list[nsd++] = *p++;
+      }
+      rtkn_list[nsd++] = 0;
+    }
+  }
+  AgString iname = AgString("init_").concat(pname);
+  if (reentrant_parser) {
+    set_my_macro("AG_INIT_PARSE", "%s%s(%s *PCB_POINTER)",
+		 VOIDB, iname.pointer(), cbt.pointer());
+    set_my_macro("AG_PARSE", "%s%s(%s *PCB_POINTER)",
+		 VOIDB, pname.pointer(), cbt.pointer());
+  }
+  else {
+    set_my_macro("AG_INIT_PARSE",
+		 "%s%s(%s)", VOIDB, iname.pointer(), voidWord);
+    set_my_macro("AG_PARSE",
+		 "%s%s(%s)", VOIDB, pname.pointer(), voidWord);
+  }
+  if (auto_init) {
+    set_my_macro("AG_INITIALIZE", "%s(%s)", iname.pointer(), pcbArg.pointer());
+  }
+  else {
+    set_my_macro("AG_INITIALIZE", "%s", "");
+  }
+
+  actual_stack_size = stack_size + parser_stack_size/2;
+  LOGV(parser_stack_size);
+  LOGV(actual_stack_size);
+  if (parser_stack_size > actual_stack_size) {
+    actual_stack_size = parser_stack_size;
+  }
+  set_my_macro("AG_PARSER_STACK_SIZE", "%d", actual_stack_size);
+
+  if ((int) grammar_type != void_token_type) {
+    cs = grammar_type->pointer();
+    wpe("\n%s %s_value(%s);\n\n",
+	cs, pname.pointer(), thisArg.pointer());
+  }
+  if (input_values) {
+    define_macro("INPUT_VALUE(type)", "*(type *) &(PCB).input_value");
+  }
+  if (Cast::nWrappers) {
+    wpe("static void ag_delete_wrappers(%s);\n", thisArg.pointer());
+    wpe("#ifndef DELETE_WRAPPERS\n"
+        "#define DELETE_WRAPPERS ag_delete_wrappers(%s)\n"
+        "#endif\n", pcbArg.pointer());
+  }
+  int token_names_printed = 0;
+  if (token_names || token_names_only) {
+    print_token_names(pname.pointer());
+    token_names_printed = 1;
+  }
+  LOGS("First token_names call point");
+  wps("\n");
+  writeEmbeddedC();
+
+  if (!case_sensitive) {
+    if (iso_latin_1) {
+      write_code_segment("toupper latin");
+    }
+    else {
+      write_code_segment("toupper");
+    }
+  }
+  else {
+    define_macro_default("CONVERT_CASE(c)", "(c)");
+  }
+  define_macro_default("TAB_SPACING", "%d", tab_spacing);
+
+  if ((int) grammar_type != void_token_type) {
+    cs = grammar_type->pointer();
+    char wrapperBuf[200];
+    sprintf(wrapperBuf, "AgObjectWrapper< %s >", cs);
+    char buf[200];
+    const char *ws = grammar_type.wrapperRequired() ? wrapperBuf : cs;
+    //sprintf(buf, stack_location, cs);
+    sprintf(buf, stack_location, ws);
+    wpe(code_segment("value proc").pointer(),
+        cs, pname.pointer(), thisArg.pointer(),
+        cs, buf);
+  }
+  LOGS("begin scanning auto procs");
+  Each<Procedure> proc;
+  for (proc.restart(); proc.loopNotFinished(); proc.getNext()) {
+    LOGV(proc) LCV(proc->form_number);
+    if (Rule(proc->form_number)->not_unique_reduction) {
+      continue;
+    }
+    if (allow_macros) {
+      proc->alias = aliasReductionProc(proc);
+    }
+  }
+
+  LOGS("second scan of auto procs");
+
+  for (proc.restart(); proc.loopNotFinished(); proc.getNext()) {
+    unsigned j;
+    char *fb, *nlp, *scp;
+    int single_line;
+    int length;
+    const char *lp, *rp;
+    int line;
+    AgString ns;
+
+    if (proc->alias) {
+      continue;
+    }
+    ns = proc_name_string(proc);
+    LOGV(proc) LCV(ns);
+    length = proc->cSegment.length();
+
+    LOGV(proc->value_flag) LCV(allow_macros);
+    fb = (char *) proc->cSegment.begin;
+    if (proc->value_flag && allow_macros && Cast::nWrappers == 0) {
+      Rule rule(proc->form_number);
+      int bias = rule->op_bias;
+      LOGV(rule) LCV(bias);
+      wpe("\n#define %s(%s",
+	  ns.pointer(), reentrant_parser? "PCB_POINTER" : "");
+      cs = reentrant_parser ? ", " : "";
+      //AgArray<RuleElement> &elementList = Rule(proc->form_number)->elementList;
+      AgArray<RuleElement> elementList = bias ? rule->hostElementList : rule->elementList;
+      for (j = 0; j < elementList.size(); j++) {
+        RuleElement &element = elementList[j];
+        if (element.cVariable == 0) {
+	  continue;
+	}
+        wpe("%s%s", cs, cVariableList[element.cVariable].pointer());
+        cs = ", ";
+      }
+      wpe(")");
+      if (length) {
+        wpe(" (");
+        while(length--) {
+          if (*fb == '\n') {
+	    pe_line_count++;
+	    parserWriteError = fputc('\\', pe_file);
+	  }
+          parserWriteError = fputc(*fb++, pe_file);
+        }
+        wpe(")");
+      }
+      wpe("\n");
+      continue;
+    }
+    LOGV(proc->value_flag);
+    if (proc->value_flag) {
+      define_proc(proc, allow_macros && Cast::nWrappers, thisArg, thisArgc);
+      cs = (const char *) proc->cSegment.begin;
+      line = proc->cSegment.line;
+      length = proc->cSegment.length();
+      while (*cs == '\n') {line++; cs++; length--;}
+      if (line_numbers) {
+        wpe("#line %d \"%s\"\n", line, ln_file_name.pointer());
+      }
+      else {
+        wpe("/* Line %d, %s */\n", line, ln_file_name.pointer());
+      }
+      if (proc->cast != void_token_type) {
+	parserWriteError = fprintf(pe_file, "  return ");
+      }
+      else {
+	parserWriteError = fprintf(pe_file, "  ");
+      }
+      while (*cs == ' ') {
+	cs++;
+	length--;
+      }
+      if (length && fwrite(cs,length,1,pe_file) == 0) {
+	parserWriteError = EOF;
+      }
+      count_pe_line(cs,length);
+      wpe(";\n");
+      if (line_numbers) {
+	wpe("#line %d \"%s\"\n", pe_line_count+2, ln_pe_name.pointer());
+      }
+      wpe("}\n");
+      continue;
+    }
+
+    nlp = strchr(fb,'\n');
+    if (nlp == NULL) {
+      nlp = fb + length;
+    }
+    char saveChar = fb[length];
+    fb[length] = 0;
+    scp = strrchr(fb,';');
+    fb[length] = saveChar;
+    if (scp == NULL) {
+      scp = fb + length;
+    }
+    single_line = allow_macros && (scp - fb <= nlp - fb);
+    while (*fb == ' ' && length) {
+      fb++;
+      length--;
+    }
+    if (single_line) {
+      single_line = (strncmp(fb, "return", 6)==0);
+    }
+    if (single_line) {
+      fb += 6;
+      length -= 6;
+      while (*fb == ' ' && length) {
+	fb++;
+	length--;
+      }
+      if (scp-fb < length) {
+	length = (int) (scp-fb);
+      }
+      single_line = (length > 0);
+    }
+    else if (allow_macros && length < (nlp - fb)) {
+      char *p = fb;
+      int k = length-6;
+      while (k >= 0) {
+        if (strncmp(p, "return", 6)== 0) {
+	  break;
+	}
+        p++;
+        k--;
+      }
+      if (k == 0) {
+        proc->cast = void_token_type;
+        single_line = 1;
+      }
+    }
+    if (single_line && Cast::nWrappers == 0) {
+      if (proc->cast == void_token_type) {
+	lp = "{", rp = "}";
+      }
+      else if (proc->cSegment.length()) {
+	lp = "(", rp = ")";
+      }
+      else {
+	lp = rp = "";
+      }
+      proc->macro_flag = 1;
+      wpe("\n#define %s(%s", 
+	  ns.pointer(), reentrant_parser? "PCB_POINTER" : "");
+      cs = reentrant_parser ? ", " : "";
+      Rule rule(proc->form_number);
+      int bias = rule->op_bias;
+      AgArray<RuleElement> elementList = bias ? rule->hostElementList : rule->elementList;
+      for (j = 0; j < elementList.size(); j++) {
+        RuleElement &element = elementList[j];
+        if (element.cVariable == 0) {
+	  continue;
+	}
+        wpe("%s%s", cs, cVariableList[element.cVariable].pointer());
+        cs = ", ";
+      }
+      wpe(") %s",lp);
+      if (length && fwrite(fb,length,1,pe_file) == 0) {
+	parserWriteError = EOF;
+      }
+      count_pe_line(fb, length);
+      wpe("%s\n", rp);
+      continue;
+    }
+    define_proc(proc, single_line && Cast::nWrappers, thisArg, thisArgc);
+    cs = (const char *) proc->cSegment.begin;
+    line = proc->cSegment.line;
+    length = proc->cSegment.length();
+    while (*cs == '\n') {
+      line++;
+      cs++;
+      length--;
+    }
+    if (line_numbers) {
+      wpe("#line %d \"%s\"\n", line, ln_file_name.pointer());
+    }
+    else {
+      wpe("/* Line %d, %s */\n", line, ln_file_name.pointer());
+    }
+    int segLength = proc->cSegment.length();
+    if (segLength && fwrite(cs,segLength,1,pe_file) == 0) {
+      parserWriteError = EOF;
+    }
+    count_pe_line(cs, proc->cSegment.length());
+    wpe("\n");
+    if (line_numbers) {
+      wpe("#line %d \"%s\"\n", pe_line_count+2, ln_pe_name.pointer());
+    }
+    wpe("}\n");
+  }
+  AgStringDictionary reductionProcedures;
+  rproc = local_array(nforms_base+1, unsigned);
+  nrproc = 0;
+  rproc[0] = 0;
+  LOGS("Starting to build reduction procedures");
+  for (i = 1; i<= nforms_base; i++) {
+    Rule rule(i);
+    Procedure proc = rule->proc_name;
+    rproc[i] = 0;
+    LOGV(rule) LCV(proc) LCV(proc.isNotNull());
+    if (proc.isNotNull()) {
+      int bias;
+      if (proc->alias) {
+	proc = proc->alias;
+      }
+      ics();
+      const char *closure = "; ";
+      LOGV(rule->not_unique_reduction) LCV(proc->cast);
+      if (rule->not_unique_reduction) {
+        if (reentrant_parser) {
+	  apprintf("ag_default(PCB_POINTER, &ag_rtt[%d]); ", rtkn_ndx[i]);
+	}
+        else {
+	  apprintf("ag_default(&ag_rtt[%d]); ", rtkn_ndx[i]);
+	}
+      }
+      Cast newValueType = proc->cast;
+      unsigned deleteIndex = 0;
+      if (proc->cast != void_token_type) {
+        LOGV(rule->elementList.size());
+        int ruleLength = rule->elementList.size();
+        Cast oldValueType = ruleLength ? rule->elementList[0].token->value_type : 0;
+        const char *format = stack_result;
+        char buf[1000];
+        if (newValueType.wrapperRequired()) {
+          deleteIndex = 1;
+          if (ruleLength && oldValueType.wrapperRequired()) {
+            //sprintf(buf,"VRO((AgObjectWrapper<%s > *), ", oldValueType->pointer());
+            sprintf(buf,"VRO(AG_WRAP_%d *, ", (int) oldValueType);
+            format = buf;
+          }
+          else {
+            format = "VNO AG_WRAP_%d(";
+          }
+          closure = "); ";
+          //apprintf(format, newValueType->pointer());
+          apprintf(format, (int)newValueType);
+        }
+        else if (ruleLength && oldValueType.wrapperRequired()) {
+          char buf[1000];
+          sprintf(buf, format, newValueType->pointer());
+          //apprintf("%sVDO((AgObjectWrapper<%s > *), ", buf, oldValueType->pointer());
+          apprintf("%sVDO(AG_WRAP_%d *, ", buf, (int) oldValueType);
+          closure = "); ";
+          deleteIndex = 1;
+        }
+        else apprintf(format, newValueType->pointer());
+      }
+      LOGV(proc_name_string(proc));
+      ass(proc_name_string(proc).pointer());
+      if (reentrant_parser) {
+        ass("(PCB_POINTER");
+        cs = ", ";
+      }
+      else {
+        acs('(');
+        cs = "";
+      }
+      bias = rule->op_bias;
+      LOGV(bias);
+      AgArray<RuleElement> elementList = bias ? rule->hostElementList : rule->elementList;
+      int nWraps = 0;
+      for (j = 0; j < elementList.size(); j++) {
+        RuleElement &element = elementList[j];
+        if (element.cVariable == 0) continue;
+        Cast cn = element.token->value_type;
+        ass(cs);
+        const char *format = stack_value;
+        if (cn.wrapperRequired()) {
+          //format = "VW(%d,(AgObjectWrapper<%s > *))";
+          apprintf("VW(%d, AG_WRAP_%d *)", j-bias, (int) cn);
+          //if (j) nWraps++;
+          if (j >= deleteIndex) nWraps++;
+        }
+        else {
+	  apprintf(format, j-bias,cn->pointer());
+	}
+        cs = ", ";
+      }
+      apprintf(")%s", closure);   //;\n            ");
+      LOGV(nWraps) LCV(Cast::nWrappers);
+      LOGV(rule->op_bias);
+      int newlineRequired = 1;
+      if (nWraps && rule->op_bias <= 0) {
+
+        //j = newValueType.wrapperRequired() ? 1 : 0;
+        //j = 1;
+        j = deleteIndex;
+        for (; j < elementList.size(); j++) {
+          RuleElement &element = elementList[j];
+          //if (element.cVariable == 0) continue;
+          Cast cn = element.token->value_type;
+          if (cn.wrapperRequired()) {
+            if (newlineRequired) {
+              newlineRequired = 0;
+              ass("\n            ");
+            }
+            LOGV(j) LCV(cn->pointer());
+            //apprintf("VWD(%d,AgObjectWrapper<%s > *); ", j,cn->pointer());
+            apprintf("VWD(%d, AG_WRAP_%d *); ", j, (int) cn);
+          }
+        }
+      }
+      ass("break;\n");
+      rproc[i] = reductionProcedures << buildAgString();
+      nrproc = i;
+    }
+    else {                      // no reduction
+      ics();
+      int k = 0;
+      unsigned j = 0;
+      Cast primaryTokenType = rule->prim_tkn->value_type;
+      if (rule->elementList.size()
+	  && (int) rule->elementList[0].token->value_type == primaryTokenType){
+        j = 1;
+      }
+      for (; j < rule->elementList.size(); j++) {
+        RuleElement &element = rule->elementList[j];
+        Cast cn = element.token->value_type;
+        if (cn.wrapperRequired()) {
+          //apprintf("VWD(%d,AgObjectWrapper<%s > *); ", j,cn->pointer());
+          apprintf("VWD(%d,AG_WRAP_%d *); ", j, (int) cn);
+          k++;
+        }
+      }
+      if (k) {
+        ass("break;\n");
+        rproc[i] = reductionProcedures << buildAgString();
+        nrproc = i;
+      }
+      else {
+	rcs();
+      }
+    }
+  }
+  wpe("\n\n");
+
+  {
+    //AgString code;
+    AgString ar_defs;
+
+    if (typedef_name != NULL) {
+      unsigned pn = 0;
+      unsigned last_enum = 0;
+      const char *new_line = ",\n  ";
+      const char *head = "typedef enum {\n";
+      headerWriteError = fprintf(h_file, head);
+      kw = 0;
+      cs = "  ";
+      for (i = 0; i++ < ntkns;) {
+        if (map_token_number[i].junky) {
+	  continue;
+	}
+        tn = map_token_number[i].token_name;
+        if (tn == 0) {
+	  continue;
+	}
+        last_enum = i;
+        template_string(Symbol(tn)->string.pointer(), 
+			enum_name.pointer(), '%');
+        char *p = string_base;
+        while (*p) {
+	  if (*p == ' ') {
+	    *p = '_';
+	  }
+	  p++;
+	}
+        if (i != pn + 1 || i== 1) {
+          apprintf(" = %d", i);
+        }
+        pn = i;
+        if (kw + tis() + 2 >= parserLineLength) {
+          kw = 0;
+	  cs = new_line;
+        }
+        headerWriteError = fputs(cs, h_file);
+        kw += 2+fps(h_file);
+        cs = ", ";
+      }
+      if (last_enum < 256 && ntkns >= 256) {
+        template_string("_last", enum_name.pointer(), '%');
+        apprintf(" = %d", ntkns);
+        if (kw + tis() + 2 >= parserLineLength) {
+          cs = new_line;
+        }
+        headerWriteError = fputs(cs,h_file);
+        fps(h_file);
+      }
+      headerWriteError = fprintf(h_file,"\n} %s;\n\n", token_type.pointer());
+
+    }
+    //code = code_segment("pcb").pointer();
+    AgString pcbHeader = code_segment("pcbHeader");
+    LOGV(pcbHeader);
+    LOGV(token_type) LCV(itt) LCV(actual_stack_size) LCV(vst);
+    headerWriteError = fprintf(h_file, pcbHeader.pointer(),
+			       structName.pointer(),
+			       token_type.pointer(),
+			       itt.pointer(),
+			       actual_stack_size, vst.pointer(),
+			       actual_stack_size);
+
+    LOGV(context_type.exists());
+    if (context_type.exists()) {
+      LOGV(context_type);
+      headerWriteError = fprintf(h_file, "  %s input_context;\n  %s cs[%d];\n",
+				 context_type.pointer(),
+				 context_type.pointer(),
+				 actual_stack_size);
+    }
+
+    LOGV(bt);
+    if (bt) {
+      headerWriteError = fprintf(h_file, "  int bts[%u], btsx;\n",
+				 actual_stack_size);
+    }
+
+    LOGV(auto_resynch);
+    if (auto_resynch) {
+      const char *ns = near_functions ? "near * near" : "*";
+      if (reentrant_parser) {
+        headerWriteError = 
+	  fprintf(h_file, "  int (%s %s*gt_procs)(struct %s *);\n"
+		  "  int (%s %s*r_procs)(struct %s *);\n"
+		  "  int (%s %s*s_procs)(struct %s *);\n",
+		  ns, constWord, structName.pointer(),
+		  ns, constWord, structName.pointer(),
+		  ns, constWord, structName.pointer());
+      }
+      else {
+        headerWriteError = 
+	  fprintf(h_file, "  int (%s %s *gt_procs)(%s);\n"
+		  "  int (%s %s*r_procs)(%s);\n"
+		  "  int (%s %s*s_procs)(%s);\n",
+		  ns, constWord, void_string,
+		  ns, constWord, void_string,
+		  ns, constWord, void_string);
+      }
+    }
+
+    if (pointer_input) {
+      LOGV(pointer_type);
+      headerWriteError = 
+	fprintf(h_file, "  %s pointer;\n  %s la_ptr;\n",
+		pointer_type.pointer(), pointer_type.pointer());
+    }
+    else if (max_key_length) {
+      headerWriteError = 
+	fprintf(h_file, "  int lab[%d], rx, fx;\n", max_key_length+1);
+    }
+
+    if (Keyword::count() > 1) {
+      headerWriteError = fprintf(h_file,
+				 "  const unsigned char *key_sp;\n"
+				 "  int save_index, key_state;\n");
+    }
+
+    if (auto_resynch) {
+      if (event_driven) {
+	ar_defs = code_segment("auto resynch event defs");
+      }
+      else {
+	ar_defs = code_segment("auto resynch defs");
+      }
+      headerWriteError = fprintf(h_file, ar_defs.pointer());
+    }
+    if (diagnose_errors) {
+      headerWriteError = fprintf(h_file, "  char ag_msg[82];\n");
+    }
+    if (semantic_productions) {
+      headerWriteError = 
+	fprintf(h_file, code_segment("reduce loop vars").pointer());
+    }
+    LOGV(cbt);
+    if (auto_resynch) {
+      headerWriteError = fprintf(h_file, "  int ag_resynch_active;\n");
+    }
+    writePCBExtensions();
+    headerWriteError = 
+      fprintf(h_file, code_segment("pcbTail").pointer(), cbt.pointer());
+
+    if (declare_pcb && !reentrant_parser) {
+      headerWriteError = 
+	fprintf(h_file, "\nextern %s %s_pcb;\n",cbt.pointer(),pname.pointer());
+    }
+  }
+  if (!really_old_style) {
+    AgString arg = reentrant_parser ? subs_template(cbt.pointer(), "$ *", '$')
+      : AgString("void");
+    //AgString arg = reentrant_parser ? "PCB_TYPE *" : "void";
+    headerWriteError = 
+      fprintf(h_file, "void init_%s(%s);\n", pname.pointer(), arg.pointer());
+    headerWriteError = 
+      fprintf(h_file, "void %s(%s);\n", pname.pointer(), arg.pointer());
+    if ((int) grammar_type != void_token_type) {
+      cs = grammar_type->pointer();
+      headerWriteError = 
+	fprintf(h_file, "\n%s %s_value(%s);\n", 
+		cs, pname.pointer(), arg.pointer());
+    }
+  }
+  else {
+    LOGV(pname);
+    headerWriteError = fprintf(h_file, "init_%s();\n", pname.pointer());
+    headerWriteError = fprintf(h_file, "%s();\n", pname.pointer());
+    if ((int) grammar_type != void_token_type) {
+      cs = grammar_type->pointer();
+      LOGV(cs);
+      headerWriteError = 
+	fprintf(h_file, "\n%s %s_value();\n", cs, pname.pointer());
+    }
+  }
+  headerWriteError = fprintf(h_file, "#endif\n\n");
+  LOGV(error_trace);
+  if (error_trace) {
+    set_my_macro("AG_TRACE_FILE", "\"%s.etr\"", simple_file_name.pointer());
+    set_my_macro("AG_TRACE_ERROR",
+		 reentrant_parser ? "ag_trace_error(PCB_POINTER);" :
+		 "ag_trace_error();");
+    write_code_segment("error trace");
+  }
+  else {
+    set_my_macro("AG_TRACE_ERROR","%s", "");
+  }
+  write_header(pname.pointer());
+  set_my_macro("AG_INPUT_TYPE", "%s", itt.pointer());
+  set_my_macro("AG_TOKEN_TYPE", "%s", token_type.pointer());
+
+  set_my_macro("AG_SAVE_CONTEXT", context_type.exists()?"GET_CONTEXT;" : "");
+  set_my_macro("AG_VALUE_STACK_TYPE", vst.pointer());
+  cs = vs = "(PCB).input_code";
+  //if (key_dict->nsx > 1) vs = cs = "*(PCB).lab";
+  if (Keyword::count() > 1) {
+    vs = cs = "*(PCB).lab";
+  }
+  if (pointer_input) {
+    vs = cs = "*(PCB).pointer";
+  }
+  ssprintf(stack_location, Cast(input_token_type)->pointer());
+  //csp = build_string();
+  AgString leftSide = buildAgString();
+  if (input_values) {
+    if (!pointer_input) {
+      vs = "(PCB).input_value";
+    }
+    set_my_macro("AG_INPUT_VALUE", vs);
+    set_my_macro("AG_GET_VALUE", "%s = %s;", leftSide.pointer(), vs);
+  }
+  else {
+    set_my_macro("AG_GET_VALUE", "%s = %s;", leftSide.pointer(), vs);
+  }
+  if (pointer_input) {
+    set_my_macro("AG_INPUT_CODE", "INPUT_CODE((%s))", cs);
+  }
+  else {
+    set_my_macro("AG_INPUT_CODE", "(%s)", cs);
+  }
+
+  define_my_macros("declare macros");
+  set_my_macro("AG_NULL", "(PCB).vs[(PCB).ssx] = ag_null_value;");
+  write_code_segment("declarations");
+  const char *nullInitializer;
+  if (typeList.size() == 1 && typeList[0] == int_token_type) {
+    nullInitializer = "0";
+  }
+  else {
+    nullInitializer = "{ 0 }";
+  }
+  LOGV(nullInitializer);
+  wpe("#ifndef NULL_VALUE_INITIALIZER\n"
+      "#define NULL_VALUE_INITIALIZER = %s\n"
+      "#endif\n\n", nullInitializer);
+  LOGV(Cast::nWrappers);
+  if (Cast::nWrappers) {
+    wpe("\nstatic %schar %sag_wdf[] = {\n  ", constWord, LOCUS);
+    kw = 2;
+    cs = "";
+    for (kits = 0; kits < nits; kits++) {
+      Token token = map_state_number[kits].char_token;
+      Cast valueType = token->value_type;
+      if (kw + 4 > parserLineLength) {kw = 2; cs = ",\n  ";}
+      //int flag = valueType.wrapperRequired();
+      int flag = valueType.wrapperRequired() ? (int) valueType : 0;
+      kw += wpe("%s%d", cs, flag);
+      cs = ", ";
+    }
+    wpe("\n};\n\n");
+    write_code_segment("wrap decls");
+    set_my_macro("MY_DELETE_WRAPPERS", "DELETE_WRAPPERS;");
+    for (Each<Cast> cast; cast.loopNotFinished(); cast.getNext()) {
+      if (!cast.wrapperRequired()) {
+	continue;
+      }
+      wpe("#undef AG_WRAP_%d\n"
+          "#define AG_WRAP_%d AgObjectWrapper<%s >\n",
+          (int) cast, (int) cast, cast->name.pointer());
+    }
+    const char *derailer = auto_resynch ?
+      "  if ((PCB).ag_resynch_active) return;\n"
+      "  (PCB).ag_resynch_active = 1;\n"
+      : "";
+
+    wpe("\nstatic void ag_delete_wrappers(%s) {\n"
+	"%s"
+        "  int sn = (PCB).sn;\n"
+        "  int sx = (PCB).ssx;\n"
+        "  while (sx--) {\n"
+        "    switch (ag_wdf[sn]) {\n",
+	thisArg.pointer(), derailer);
+    for (Each<Cast> c; c.loopNotFinished(); c.getNext()) {
+      if (!c.wrapperRequired()) continue;
+/*
+      wpe("      case %d: {\n"
+          "        AgObjectWrapper<%s > *wrapper = (AgObjectWrapper<%s > *) (&(PCB).vs[sx]);\n"
+          "        delete wrapper;\n"
+          "        break;\n"
+          "      }\n", (int) c, c.name().pointer(), c.name().pointer());
+*/
+      wpe("      case %d: ag_delete_object((AG_WRAP_%d *) &(PCB).vs[sx]); break;\n",
+          (int) c, (int) c);
+    }
+    wpe("      default: break;\n"
+        "    }\n"
+        "    sn = (PCB).ss[sx];\n"
+        "  }\n}\n\n");
+#if 0 /* BAD */
+    if (error_token != 0)  {
+      set_my_macro("DELETE_OBJECT", "ag_delete_object(%s);", pcbArg.pointer());
+      wpe("void ag_delete_object(%s) {\n"
+          "  int sn = PCB.sn;\n"
+          "  (PCB).sn = (PCB).ss[--(PCB).ssx];\n"
+					"  if (ag_wdf[sn]) delete (AgWrapper *) (&(PCB).vs[(PCB).ssx]);\n"
+					"}\n\n"
+					, thisArg.pointer());
+    }
+#endif
+    if (error_token != 0)  {
+      set_my_macro("DELETE_OBJECT", "ag_delete_object(%s);", pcbArg.pointer());
+      wpe("void ag_delete_object(%s) {\n"
+          "  int sn = PCB.sn;\n"
+          "  (PCB).sn = (PCB).ss[--(PCB).ssx];\n"
+          "  switch (ag_wdf[sn]) {\n", thisArg.pointer());
+      for (Each<Cast> c; c.loopNotFinished(); c.getNext()) {
+        if (!c.wrapperRequired()) {
+	  continue;
+	}
+/*
+        wpe("    case %d:  delete (AgObjectWrapper<%s > *) (&(PCB).vs[(PCB).ssx]); break;\n",
+            (int) c,  (c.name().pointer()));
+*/
+        wpe("    case %d:  {\n"
+            "      AG_WRAP_%d *wrapper = (AG_WRAP_%d *) (&(PCB).vs[(PCB).ssx]);\n"
+            "      delete wrapper;\n"
+            "       break;\n"
+            "     }\n",
+            (int) c,  (int) c, (int) c);
+      }
+      wpe("    default: break;\n"
+          "  }\n}\n\n");
+    }
+  }
+  else {
+    set_my_macro("MY_DELETE_WRAPPERS", "%s", "");
+    set_my_macro("DELETE_OBJECT","(PCB).sn = (PCB).ss[--(PCB).ssx];");
+  }
+  wpe("static %s %sag_null_value NULL_VALUE_INITIALIZER;\n", 
+      vst.pointer(), constWord);
+  set_my_macro("AG_INPUT", stack_location, Cast(input_token_type)->pointer());
+
+  type = Procedure::count() + 1 <= 255 ? "char" : 
+    Procedure::count() + 1 <= 65535 ? "short" : "int";
+  wpe("\nstatic %sunsigned %s %sag_rpx[] = {\n  ",constWord, type, LOCUS);
+  kw = 4;
+  cs = "";
+  for (i = 0; i <= nrproc; i++) {
+    if (kw + 4 > parserLineLength) {
+      kw = 0;
+      cs = ",\n  ";
+    }
+    n = rproc[i];
+    kw += wpe("%s%3d", cs, n);
+    cs = ",";
+  }
+  wps("\n};\n");
+
+  //if (key_dict->nsx > 1) write_key_tables();
+  if (Keyword::count() > 1) {
+    write_key_tables();
+  }
+
+  if (max_char_number >= min_char_number) {
+
+    if (min_char_number) {
+      ssprintf(" + %d", -min_char_number);
+    }
+    else {
+      ics();
+    }
+    acs(0);
+    int charType = 0;
+    if (test_range && pointer_input) {
+      int n = strlen(type);
+      char *type = pointer_type.pointer();
+      char ch = '*';
+      while (type[n-1] == ch) {
+	type[n-1] = 0;
+	n--;
+	ch = ' ';
+      }
+      if (n > 5) {
+	type += n - 5;
+      }
+      if (*type == ' ' || *type == '\t') {
+	type++;
+      }
+      charType = strcmp(type, "char") == 0;
+    }
+    if (test_range &&
+       (!pointer_input || charType)) {
+      wpe("#define AG_TCV(x) (((int)(x) >= %d && (int)(x) <= %d) ? ag_tcv[(x)%s] : 0)\n",
+         min_char_number, max_char_number, string_base);
+    }
+    else {
+      wpe("\n#define AG_TCV(x) ag_tcv[(x)%s]\n",string_base);
+    }
+    rcs();
+    type = ntkns <= 255 ? "char" : ntkns <= 65535 ? "short" : "int";
+    wpe("\nstatic %sunsigned %s %sag_tcv[] = {\n  ", constWord, type, LOCUS);
+    kw = 4;
+    cs = "";
+    for (i = 0; i < n_chars; i++) {
+      tn = map_char_number[i].token_number;
+      if (kw + 4 > parserLineLength) {
+	kw = 0;
+	cs = ",\n  ";
+      }
+      kw += wpe("%s%3d", cs, tn);
+      cs = ",";
+    }
+    wps("\n};\n\n");
+  }
+  else if (test_range) {
+    wpe("#define AG_TCV(x) (((x) > 0 && (x) <= %d) ? (x) : 0)\n", ntkns);
+  }
+  else {
+    wpe("\n#define AG_TCV(x) (x)\n");
+  }
+  cs = really_old_style ? "function macro defs old style" :
+    "function macro defs";
+  define_my_macros(cs);
+  gen_input_code();
+
+
+  if (bt) {
+    if (auto_resynch || error_token) {
+      set_my_macro("AG_EOF", "%d", eof_token);
+    }
+    if (error_token) {
+      set_my_macro("AG_ERROR", "%d", error_token);
+    }
+    csp = "backtrack on";
+    cs = "backtrack on macros";
+    define_my_macros(context_type.exists() ? "context macros" : 
+		     "no context macros");
+    define_my_macros(error_token ? "error resynch undo" : "undo macro");
+  }
+  else {
+    csp = "backtrack off";
+    cs = "backtrack off macros";
+  }
+  define_my_macros(cs);
+  write_code_segment(csp);
+
+  if (rtkn_count) {
+    wpe("\nstatic %sint %sag_rtt[] = {\n  ", constWord, LOCUS);
+    kw = 4;
+    cs = "";
+    for (i = 0; i < rtkn_count; i++) {
+      if (kw + 4 > parserLineLength) {
+	kw = 0;
+	cs = ",\n  ";
+      }
+      kw += wpe("%s%3d", cs, rtkn_list[i]);
+      cs = ",";
+    }
+    wps("\n};\n\n");
+  }
+
+  write_parse_table_tokens();
+  write_parse_table_actions();
+  write_parse_table_params();
+
+  fclose(h_file);
+  if (headerWriteError == EOF) {
+    errorList.push(Error("Error writing header file"));
+  }
+  LOGS("h_file closed");
+
+  // Up through 2.0, this was <= 255, ignoring "engine_index+1" 9 lines down.
+  type = max_engine_index < 255 ? "char" : 
+    max_engine_index < 65535 ? "short" : "int";
+
+  wpe("\nstatic %sunsigned %s %sag_sbt[] = {\n", constWord, type, LOCUS);
+  kw = 8;
+  wps("     0");
+  cs = ",";
+
+  LOGS("Write sbt table");
+  for (i = 1; i <= nstates; i++) {
+    n = map_state_number[i - 1].engine_index + 1;
+    if (kw + 6 > parserLineLength) {
+      kw = 0;
+      cs = ",\n  ";
+    }
+    kw += wpe("%s%4d", cs, n);
+    cs = ",";
+  }
+  wps("\n};\n\n");
+
+  wpe("\nstatic %sunsigned %s %sag_sbe[] = {\n",constWord, type, LOCUS);
+  kw = 8;
+  wpe("  %4d", map_state_number[0].n_terminals);
+  cs = ",";
+
+  LOGS("Write sbe table");
+  for (i = 1; i < nstates; i++) {
+    n = map_state_number[i-1].engine_index + 1 + 
+      map_state_number[i].n_terminals;
+    if (kw + 6 > parserLineLength) {
+      kw = 0;
+      cs = ",\n  ";
+    }
+    kw += wpe("%s%4d", cs, n);
+    cs = ",";
+  }
+  wpe("%s%4d", cs, map_state_number[nstates - 1].engine_index + 1);
+  wps("\n};\n\n");
+
+  LOGS("Write ag_fl table");
+  wpe("\nstatic %sunsigned char %sag_fl[] = {\n  ", constWord, LOCUS);
+  kw = 4;
+  cs = "";
+  for (i = 0; i <= nforms_base; i++) {
+    if (kw + 4 > parserLineLength) {
+      kw = 0;
+      cs = ",\n  ";
+    }
+    n = Rule(i)->length();
+    kw += wpe("%s%1d", cs, n);
+    cs = ",";
+  }
+  wps("\n};\n");
+
+  LOGS("Write ag_ptt table");
+  type = ntkns <= 255 ? "char" : ntkns <= 65535 ? "short" : "int";
+  wpe("\nstatic %sunsigned %s %sag_ptt[] = {\n  ", constWord, type, LOCUS);
+
+  kw = 4;
+  cs = "";
+  for (i = 0; i <= nforms_base; i++) {
+    if (kw + 4 > parserLineLength) {
+      kw = 0;
+      cs = ",\n  ";
+    }
+    n = Rule(i)->prim_tkn;
+    kw += wpe("%s%3d", cs, n);
+    cs = ",";
+  }
+  wps("\n};\n\n");
+
+  LOGS("Define macros");
+  {
+    define_my_macros(
+      auto_resynch ? "auto resynch macros" :
+      error_token ? "error resynch macros" :
+      "parse action macros");
+    if (auto_resynch) {
+      set_my_macro("AG_RESYNCH", 
+		   reentrant_parser ? "ag_auto_resynch(PCB_POINTER);" : 
+		   "ag_auto_resynch();");
+    }
+    define_my_macros(
+      semantic_productions ? "reduce macros checking" : "reduce macros");
+    write_code_segment(
+      semantic_productions ? "reduce loop checking" : "reduce loop");
+    if (semantic_productions && reduction_choices) {
+      write_code_segment("reduction choices");
+    }
+  }
+  //wpe("\nstatic %s%sag_ra(%s)\n{\n", VOIDB, nearWord, voidWord);
+  wpe("\nstatic %s%sag_ra(%s)\n{\n", VOIDB, nearWord, thisArg.pointer());
+  LOGS("Write ag_ra table");
+  if (reductionProcedures.size() > 1) {
+    wpe("  switch(ag_rpx[(PCB).ag_ap]) {\n");
+    for (i = 1; i < reductionProcedures.size(); i++) {
+      LOGV(i) LCV(reductionProcedures[i]);
+      wpe("    case %d: %s", i, reductionProcedures[i].pointer());
+    }
+    wpe("  }\n");
+    if (pointer_input) {
+      wpe("  (PCB).la_ptr = (PCB).pointer;\n");
+    }
+  }
+  wpe("}\n\n");
+
+  if (diagnose_errors && !token_names && !token_names_printed) {
+    print_token_names(pname.pointer());
+  }
+  LOGS("Second token_names call point");
+  if (auto_resynch || (diagnose_errors && error_frame)) {
+    LOGSECTION("ag_ctn option");
+    int *ip, *iq = local_array(2*nstates, int);
+    int maxToken = 0;
+
+    //nf = 0;
+    ip = iq;
+    for (i = 0; i < nstates; i++) {
+      int token = find_ctn(i);
+      if (token > maxToken) {
+	maxToken = token;
+      }
+      *ip++ = token;
+      *ip++ = frameIndex;
+    }
+    ip = iq;
+
+    type = maxToken <= 255 ? "char" : maxToken <= 65535 ? "short" : "int";
+    wpe("\nstatic %sunsigned %s %sag_ctn[] = {\n",  constWord, type, LOCUS);
+    cs = "  ";
+    kw = 2;
+    for (i = 0; i < nstates; i++) {
+      int f;
+      if (kw + 6 > parserLineLength) {
+	kw = 0;
+	cs = ",\n  ";
+      }
+      f = *ip++;
+      n = *ip++;
+      kw += wpe("%s%3d,%1d", cs, f, n);
+      cs = ",";
+    }
+    wps("\n};\n\n");
+  }
+  LOGS("ag_ctn loop complete");
+
+  if (diagnose_errors) {
+    write_code_segment("diagnose defs");
+    const char *codeSeg = character_seen ? "diagnose char" : "diagnose token";
+    set_my_macro("AG_DIAGNOSE",
+		 reentrant_parser ? "ag_diagnose(PCB_POINTER);" :
+		 "ag_diagnose();");
+    set_my_macro("AG_INIT_ERROR_MESSAGE", "%s", "");
+    write_code_segment(codeSeg);
+    if (error_frame) {
+      const char *contextCode = "diagnose context";
+      //if (lines_and_columns) contextCode = "diagnose context col";
+      write_code_segment(contextCode);
+    }
+    wps("}\n");
+  }
+  else {
+    set_my_macro("AG_DIAGNOSE", "%s", "");
+    set_my_macro("AG_INIT_ERROR_MESSAGE",
+		 "(PCB).error_message = \"Syntax Error\";");
+  }
+
+  cs = (auto_resynch || error_token) && event_driven ?
+    "ag_action_12_proc" : "ag_action_8_proc";
+
+  set_my_macro("AG_RECOVER", cs);
+
+  write_code_segment("reduce proc defs");
+  if (error_token) {
+    write_code_segment("reduce proc error defs");
+  }
+  cs = error_token ? "error resynch" :
+    auto_resynch ? "auto resynch": NULL;
+  if (cs != NULL) {
+    set_my_macro("AG_ERROR_STATE", "%d", nstates);
+    if (auto_resynch) {
+      write_code_segment("jns proc");
+    }
+    else if (reentrant_parser) {
+      set_my_macro("AG_RESYNCH", "ag_error_resynch(PCB_POINTER);");
+    }
+    else {
+      set_my_macro("AG_RESYNCH", "ag_error_resynch();");
+    }
+    sss(cs);
+    if (event_driven) {
+      ass(" token sink mode");
+    }
+    write_code_segment(string_base);
+    rcs();
+  }
+  else {
+    define_my_macros("no resynch");
+  }
+
+
+  {
+    cs = "parse procs";
+    if (event_driven) {
+      cs = "chain parse procs";
+    }
+    write_code_segment(cs);
+    if (error_token)  {
+      cs = "error token parse procs";
+      if (event_driven) {
+	cs = "chain error token parse procs";
+      }
+      write_code_segment(cs);
+    }
+    if (auto_resynch) {
+      write_code_segment("error parse procs");
+    }
+
+    cs = "parse engine";
+    if (event_driven) {
+      cs = "chain parse engine";
+      //if (key_dict->nsx > 1) {
+      //  cs = "chain key parse engine";
+      //}
+      if (Keyword::count() > 1) {
+	cs = "chain key parse engine";
+      }
+    }
+    write_code_segment(cs);
+  }
+  if (cSegmentStack.size() == 0 && main_program 
+      && !pointer_input && !event_driven && !reentrant_parser) {
+    wpe(
+      "\nint main(%s) {\n"
+      "  %s();\n"
+      "  return 0;\n"
+      "}\n", voidWord, pname.pointer());
+  }
+  fclose(pe_file);
+  if (parserWriteError == EOF) {
+    errorList.push(Error("Error writing parser file"));
+  }
+  for (i = 1; i < my_macros->nsx; i++) {
+    DEALLOCATE(my_macros_subs[i]);
+    my_macros_subs[i] = NULL;
+  }
+  my_macros = delete_string_dict(my_macros);
+  syntax_state = engine_built;
+}
+
+