view anagram/agcore/bpe3.cpp @ 21:1c9dac05d040

Add lint-style FALLTHROUGH annotations to fallthrough cases. (in the parse engine and thus the output code) Document this, because the old output causes warnings with gcc10.
author David A. Holland
date Mon, 13 Jun 2022 00:04:38 -0400
parents 13d2b8934445
children
line wrap: on
line source

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