view anagram/agcore/ftpar.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.
 *
 * ftpar.cpp
 */

#include "agarray.h"
#include "arrays.h"
#include "bpe3.h"
#include "cd.h"
#include "config.h"
#include "csexp.h"
#include "dict.h"
#include "ftpar.h"
#include "keyword.h"
#include "q1glbl.h"
#include "rule.h"
#include "token.h"
#include "tsd.h"

//#define INCLUDE_LOGGING
#include "log.h"


AgArray<unsigned> traceCounts;

//static dc_ref trace_count_display;


int precedes(cint a, cint b) {
  if (a.y < b.y) return 1;
  if (a.y > b.y) return 0;
  if (a.x < b.x) return 1;
  return 0;
}


FtParser::FtParser(AgString t)
  : text(t)
  , state(t.pointer())
  , initialStack(0)
  , lookAhead(state.pointer)
  , endPointer(state.pointer +
	       (state.pointer != 0 ? strlen((const char *) state.pointer) : 0))
  , inputToken(0)
  , nNullShifts(0)
  , processState(ready)
  , ruleToReduce(0)
  , reductionSelection(0)
  , stackChanged(*this)
  , testingKeyword(0)
{
  LOGSECTION("FtParser::FtParser");
  LOGV((int) state.pointer);
  LOGV((int) lookAhead);
  getToken();
  if (!traceCounts.exists()) {
    traceCounts = AgArray<unsigned>(nforms_base + 1);
    memset(traceCounts.pointer(), 0, sizeof(unsigned)*traceCounts.size());
/*
    trace_count_display =
      dc_ref(new rule_count_dc("Trace Coverage", traceCounts));
*/
  }
}

FtParser::FtParser()
  : initialStack(0)
  , lookAhead(0)
  , inputToken(0)
  , nNullShifts(0)
  , processState(ready)
  , ruleToReduce(0)
  , reductionSelection(0)
  , stackChanged(*this)
  , testingKeyword(0)
{
  LOGSECTION("FtParser::FtParser");
  LOGV((int) state.pointer);
  LOGV((int) lookAhead);
  if (!traceCounts.exists()) {
    traceCounts = AgArray<unsigned>(nforms_base + 1);
    memset(traceCounts.pointer(), 0, sizeof(unsigned)*traceCounts.size());
/*
    trace_count_display =
      dc_ref(new rule_count_dc("Trace Coverage", traceCounts));
*/
  }
}

FtParser::FtParser(tsd *initialStack_)
  : initialStack(initialStack_ ? copy_tuple_set(initialStack_) : 0)
  , lookAhead(0)
  , inputToken(0)
  , nNullShifts(0)
  , processState(ready)
  , ruleToReduce(0)
  , reductionSelection(0)
  , stackChanged(*this)
  , testingKeyword(0)
{
  LOGSECTION("FtParser::FtParser");
  LOGV((int) state.pointer);
  LOGV((int) lookAhead);
  LOGV((int) initialStack);
  if (initialStack) {
    LOGV(initialStack->nt);
    for (unsigned i = 0; i < initialStack->nt; i++) {
      unsigned sn, tn;
      xtx(initialStack,i, &sn, &tn);
      stateStack.push(State(sn,tn));
    }
    state = stateStack.pop();
  }
  LOGV(stateStack.size());
  if (!traceCounts.exists()) {
    traceCounts = AgArray<unsigned>(nforms_base + 1);
    memset(traceCounts.pointer(), 0, sizeof(unsigned)*traceCounts.size());
/*
    trace_count_display =
      dc_ref(new rule_count_dc("Trace Coverage", traceCounts));
*/
  }
}

FtParser::~FtParser() {
  if (initialStack) {
    delete_tsd(initialStack);
  }
}

FtParser &FtParser::reset() {
  LOGSECTION("FtParser::reset");
  stateStack.discardData();
  auxStack.discardData();
  transStack.discardData();
  LOGV(auxStack.size());
  LOGV((int) initialStack);
  inputToken = 0;
  if (initialStack) {
    LOGV(initialStack->nt);
    for (unsigned i = 0; i < initialStack->nt; i++) {
      unsigned sn, tn;
      xtx(initialStack,i, &sn, &tn);
      stateStack.push(State(sn, tn));
    }
    state = stateStack.pop();
  }
  else {
    state = State(text.pointer());
    lookAhead = state.pointer;
    endPointer = state.pointer +
      (state.pointer != 0 ? strlen((const char *) state.pointer) : 0);
    getToken();
  }
  LOGV(stateStack.size());
  reductionState = State();
  nNullShifts = 0;
  processState = ready;
  //if ((dc *)displayControl) {
  //  displayControl->des->d_size.y = stateStack.size();
  //}
  stackChanged(stateStack.size() + 1);
  return *this;
}

void FtParser::track(void) {
  LOGSECTION("FtParser::track");
  LOGV((int) state.pointer);
  LOGV((int) lookAhead);
  if (lookAhead != 0) {
    while (state.pointer < lookAhead) {
      switch (*state.pointer++) {
	case '\n':
	  if (*state.pointer) state.column = state.charPos = 0, state.line++;
	case '\f':
	  break;
	case '\t':
	  state.column += tab_spacing - state.column % tab_spacing;
	  state.charPos++;
	  break;
	default:
	  state.column++;
	  state.charPos++;
      }
    }
  }
  state.token = inputToken = 0;
  auxStack.discardData();
  transStack.discardData();
  LOGV(auxStack.size());
  nNullShifts = 0;
  LOGV(state.line) LCV(state.column);
}

void FtParser::shiftTerminalAndAccept(void) {
  LOGSECTION("FtParser::shiftTerminalAndAccept");
  processState = finished;
  track();
}

void FtParser::shiftTerminal() {
  LOGSECTION("FtParser::shiftTerminal");
  LOGV(state.number) LCV(state.token) LCV((int) state.pointer);
  LOGV(stateStack.size());
  state.token = map_state_number[actionParameter].char_token;
  stateStack.push(state);
  state.number = actionParameter;
  track();
  LOGV(state.token);
  LOGV(state.number) LCV((int) state.pointer);
  LOGV(stateStack.size());
}

void FtParser::requestSelection(int actionParameter) {
  LOGSECTION("FtParser::requestSelection");
  processState = selectionRequired;
  ruleToReduce = actionParameter;
  reductionSelection = 0;
  while (!validSelection(reductionSelection, reductionState.number)) {
    reductionSelection++;
  }
  LOGV(ruleToReduce);
  LOGV(reductionIndex);
  LOGV(state.number) LCV((int) state.pointer);
  LOGV(reductionState.number);
  reductionState.token = ibnfs[ibnfb[ruleToReduce]];
  LOGV(stateStack.size());
}

/*
 * State stack discipline
 *   reduce
 *     If n is the length of the rule, n states are popped from the
 *     state stack.
 *     If n > 0, the state number becomes the state number of the
 *     last state popped.
 *
 *   shiftTerminalAndReduce
 *     If n is the length of the rule, n-1 states are popped from
 *     the state stack.
 *     If n > 1, the state number becomes the state number of the
 *     last state popped.
 *
 *   shiftNonterminalAndReduce
 *     If n is the length of the rule, n-1 states are popped from
 *     the state stack.
 *     If n > 1, the state number becomes the state number of the
 *     last state popped.
 *
 *   shiftNull
 *     The current state is pushed to the state stack and the
 *     new state number is given by the action parameter.
 *
 *   shiftNonterminal
 *     The current state is pushed to the state stack and the
 *     new state number is given by the action parameter
 */

void FtParser::shiftTerminalAndReduce() {
  LOGSECTION("FtParser::shiftTerminalAndReduce");
  LOGV(state.number) LCV(state.token) LCV((int) state.pointer);
  LOGV(location());
  LOGV(actionParameter);
  //form_number_map *fp = &map_form_number[actionParameter];
  Rule rule(actionParameter);
  RuleDescriptor &ruleDescriptor(rule);
  //ruleLength = rule->length();
  ruleLength = ruleDescriptor.length();
  LOGV(ruleLength);
  if (actionParameter <= nforms_base) {
    traceCounts[actionParameter]++;
  }
  int nStackedStates = ruleLength - 1;
  if (nStackedStates > 0) {
    reductionState = stateStack[stateStack.size() - nStackedStates];
  }
  else {
    reductionState = state;
  }
  track();
  if (ibnfn[actionParameter] > 1) {
    reductionIndex = stateStack.size() - (ruleLength - 1);
    assert((unsigned) reductionIndex <= (unsigned) stateStack.size());
    requestSelection(actionParameter);
    return;
  }
  assert((unsigned)nStackedStates <= stateStack.size());
  stateStack.discardData(nStackedStates);
  //reductionState.token = rule->prim_tkn;
  reductionState.token = ruleDescriptor.prim_tkn;
  state.number = reductionState.number;
  dispatchReductionToken();
  LOGV(location());
  LOGV(state.number);
  LOGV(state.token) LCV((int) state.pointer);
  auxStack.discardData();
  transStack.discardData();
  LOGV(auxStack.size());
}

void FtParser::reduce(void) {
  LOGSECTION("FtParser::reduce");
  LOGV(state.number) LCV(state.token) LCV((int) state.pointer);
  //form_number_map *fp = &map_form_number[actionParameter];
  Rule rule(actionParameter);
  RuleDescriptor &ruleDescriptor(rule);
  //ruleLength = rule->length();
  ruleLength = ruleDescriptor.length();
  LOGV(actionParameter);
  LOGV(ruleLength);
  LOGV(stateStack.size());
  if (actionParameter <= nforms_base) {
    traceCounts[actionParameter]++;
  }
  if (ruleLength) {
    reductionState = stateStack[stateStack.size() - ruleLength];
  }
  else {
    reductionState = state;
  }
  if (ibnfn[actionParameter] > 1) {
    reductionIndex = stateStack.size() - ruleLength;
    assert((unsigned) reductionIndex <= (unsigned) stateStack.size());
    requestSelection(actionParameter);
    return;
  }
  int k = ruleLength;
  transStack.push(Transaction(k, state));
  LOGV(transStack.size());
  LOGV(k) LCV(state.number) LCV(state.token);
  LOGV(nNullShifts);
  while (k--) {
    LOGV(stateStack.top().number) LCV(stateStack.top().token) 
      LCV((int) state.pointer);
    auxStack.push(stateStack.pop());
  }
  LOGV(auxStack.size());
  //reductionState.token = rule->prim_tkn;
  reductionState.token = ruleDescriptor.prim_tkn;
  state.number = reductionState.number;
  LOGV(reductionState.token);
  LOGV(stateStack.size());
  dispatchReductionToken();
  LOGV(stateStack.size());
  if (lookAhead) {
    lookAhead = state.pointer;
  }
  state.token = 0;
}

void FtParser::skip(void) {
  LOGSECTION("FtParser::skip");
  LOGV(state.number) LCV(state.token);
  if (actionParameter <= nforms_base) {
    traceCounts[actionParameter]++;
  }
  track();
}

void FtParser::shiftNull(void) {
  LOGSECTION("FtParser::shiftNull");
  LOGV(state.number) LCV(state.token) LCV((int) state.pointer);
  transStack.push(Transaction(-1, state));
  LOGV(transStack.size());
  state.token = map_state_number[actionParameter].char_token;
  stateStack.push(state);
  state.number = actionParameter;
  if (lookAhead) {
    lookAhead = state.pointer;
  }
  state.token = 0;
}

void FtParser::error() {
  LOGSECTION("FtParser::error");
  LOGV(stateStack.size());
  LOGV(state.number) LCV(state.token) LCV((int) state.pointer);
  LOGV(state.number) LCV(state.token) LCV((int) state.pointer);
  LOGV(auxStack.size());
  int k = transStack.size();
  LOGV(transStack.size());
  while (k--) {
    Transaction trans = transStack.pop();
    int n = trans.count;
    LOGV(trans.count) LCV(trans.state.number) LCV(trans.state.token);
    while (n < 0) {
      stateStack.pop();
      n++;
    }
    while (n > 0) {
      stateStack.push(auxStack.pop());
      n--;
    }
    state.number = trans.state.number;
    state.token = trans.state.token;
    LOGV(trans.count) LCV(state.number) LCV(state.token);
  }
  auxStack.discardData();
  transStack.discardData();
  processState = syntaxError;
  if (lookAhead) {
    lookAhead = state.pointer;
  }
  state.token = 0;
}


void FtParser::accept(void) {
  LOGSECTION("FtParser::accept");
  state.number = stateStack.top().number;
  state.token = stateStack.top().token;
  stateStack.pop();
  LOGV(stateStack.size());
  processState = finished;
}

int FtParser::shiftNonterminal() {
  LOGSECTION("FtParser::shiftNonterminal");
  LOGV(state.number) LCV(state.token) LCV((int) state.pointer);
  transStack.push(Transaction(-1, state));
  LOGV(transStack.size());
  LOGV(actionParameter) LCV(nstates);
  reductionState.token = map_state_number[actionParameter].char_token;
#ifdef INCLUDE_LOGGING
  ics();
  atkn(reductionState.token);
  LOGV(reductionState.token) LCV(string_base);
  rcs();
#endif
  stateStack.push(reductionState);
  state.number = actionParameter;
  LOGV(stateStack.size());
  LOGV(state.number) LCV((int) state.pointer);
  return 0;
}

int FtParser::shiftNonterminalAndReduce() {
  LOGSECTION("FtParser::shiftNonterminalAndReduce");
  LOGV(location());
  LOGV(reductionState.number) LCV(reductionState.token);
  //form_number_map *fp = &map_form_number[actionParameter];
  Rule rule(actionParameter);
  RuleDescriptor &ruleDescriptor(rule);
  //ruleLength = rule->length();
  ruleLength = ruleDescriptor.length();
  LOGS("rule number") LCV(actionParameter);
  LOGV(ruleLength);
  LOGV(rule->prim_tkn);
  LOGV(stateStack.size());
  if (actionParameter <= nforms_base) {
    traceCounts[actionParameter]++;
  }
  int nStackedStates = ruleLength - 1;
  if (nStackedStates > 0) {
    reductionState = stateStack[stateStack.size() - nStackedStates];
  }
  if (ibnfn[actionParameter] > 1) {
    reductionIndex = stateStack.size() - (ruleLength - 1);
    assert((unsigned) reductionIndex <= (unsigned) stateStack.size());
    requestSelection(actionParameter);
    return 0;
  }
  transStack.push(Transaction(nStackedStates, state));
  LOGV(transStack.size());
  LOGV(nStackedStates) LCV(state.number) LCV(state.token);
  LOGV(auxStack.size());
  while (nStackedStates--) {
    LOGV(stateStack.top().number) LCV(stateStack.top().token) 
      LCV((int) state.pointer);
    auxStack.push(stateStack.pop());
  }
  LOGV(auxStack.size());
  //reductionState.token = rule->prim_tkn;
  reductionState.token = ruleDescriptor.prim_tkn;
  state.number = reductionState.number;
  LOGV(location());
  LOGV(stateStack.size());
  return 1;
}

int FtParser::shiftNonterminalAndAccept() {
  LOGSECTION("FtParser::shiftNonTerminalAndAccept");
  state.number = reductionState.number;
  state.token = reductionState.token;
  LOGV(state.number);
  LOGV(state.token) LCV((int) state.pointer);
  processState = finished;
  return 0;
}

int (FtParser::*FtParser::nonterminalAction[4])() = {
  &FtParser::shiftNonterminalAndAccept,
  &FtParser::shiftNonterminal,
  &FtParser::shiftNonterminalAndReduce,
  &FtParser::shiftNonterminalAndReduce
};

void (FtParser::*FtParser::terminalAction[11])() = {
  &FtParser::shiftTerminalAndAccept,
  &FtParser::shiftTerminal,
  &FtParser::shiftTerminalAndReduce,
  &FtParser::shiftTerminalAndReduce,
  &FtParser::reduce,
  &FtParser::reduce,
  &FtParser::accept,
  &FtParser::error,
  &FtParser::shiftNull,
  &FtParser::skip,
  &FtParser::skip
};

static int different(const unsigned char *s1, const unsigned char *s2, unsigned n) {
  LOGSECTION("different");
  LOGV(n);
  //if (n > strlen((const char *)s2)) return 1;
  if (case_sensitive) {
    return strncmp((const char *)s1, (const char *) s2, n);
  }
  while (n--) {
    if (agToUpper(*s1++) != agToUpper(*s2++)) {
      return 1;
    }
  }
  return 0;
}


Token FtParser::keyToken(void) {
  LOGSECTION("FtParser::keyToken");
  int matchLength = 0;
  //int key = 0;
  Keyword key;
  int keyListNumber = map_state_number[state.number].key_list;
  if (keyListNumber == 0) {
    return Token();
  }

  int *keyList = dict_str(key_list_dict, keyListNumber);
  int nKeys = *keyList++ - 1;
  LOGV(lookAhead) LCV(nKeys);
  while (nKeys--) {
    Keyword keyNumber = map_token_number[*keyList++].key;
    KeywordDescriptor &keyDescriptor(keyNumber);
    //unsigned char *keyString = (unsigned char *)keyNumber->string.pointer();
    unsigned char *keyString = (unsigned char *)keyDescriptor.string.pointer();
    //AgString keyString = keyNumber->string;
    int length = strlen((const char *) keyString);
    //int length = keyString.size();
    if (length <= matchLength) {
      continue;
    }
    if ((lookAhead + length) > endPointer) {
      continue;
    }
    if (different(keyString, lookAhead, length)) {
      continue;
    }
    //if (different((unsigned char *)keyString.pointer(), lookAhead, length)) {
    //  continue;
    //}
    //int charSetNumber = keyNumber->reserve;
    int charSetNumber = keyDescriptor.reserve;
    if (charSetNumber) {
      int *reservedCharSet = dict_str(char_set_dict, charSetNumber);
      int nReservedCharSet = *reservedCharSet++ - 1;
      if (lookAhead + length < endPointer) {
        unsigned char fc = state.pointer[length];
        while (nReservedCharSet && fc != *reservedCharSet++) {
	  nReservedCharSet--;
	}
        if (nReservedCharSet) {
	  continue;
	}
      }
    }
    matchLength = length;
    key = keyNumber;
  }
  LOGV((int) lookAhead) LCV(matchLength);
  LOGV(key) LCV(testingKeyword);
  if (key.isNotNull() && (int) key != testingKeyword) {
    lookAhead = state.pointer + matchLength;
    LOGV(key->token_number);
    return key->token_number;
  }
  return Token();
}


void FtParser::getToken() {
  LOGSECTION("FtParser::getToken");
  LOGV(stateStack.size());
  LOGV(state.number) LCV((int) state.pointer);
  LOGV((int) lookAhead);
  LOGV((int) endPointer);
  LOGV(inputToken);
  if (inputToken) {
    state.token = inputToken;
  }
  else if (lookAhead != 0 && lookAhead >= endPointer) {
    if (text.exists() && eof_token) {
      token_number_map &eofTokenMap = map_token_number[eof_token];
      //if (map_token_number[eof_token].non_terminal_flag) {
      if (eofTokenMap.non_terminal_flag) {
        //unsigned charSet = map_token_number[eof_token].token_set_id;
        unsigned charSet = eofTokenMap.token_set_id;
        unsigned *list = (unsigned *) dict_str(char_set_dict,charSet);
        int character = list[1];
        LOGV(charSet);
        LOGV(character);
        state.token = map_char_number[character].token_number;
      }
      else {
	state.token = eof_token;
      }
      LOGV(state.token) LCV((int) state.pointer);
    }
    else {
      processState = unexpectedEndOfFile;
      state.token = 0;
    }
  }
  else if (lookAhead != 0) {
    state.token = keyToken();
    LOGV(state.token) LCV((int) state.pointer);
    if (state.token == 0) {
      state.token = 
	map_char_number[*lookAhead++ - min_char_number].token_number;
    }
  }
  LOGV(state.token) LCV((int) state.pointer);
  LOGV(auxStack.size());
}

/*
unsigned FtParser::inspectToken() {
  LOGSECTION("FtParser::inspectToken");
  LOGV((int) lookAhead);
  LOGV((int) endPointer);
  unsigned token = 0;
  if (inputToken) {
    token = inputToken;
  }
  else if (lookAhead !=0 && lookAhead >= endPointer) {
    if (text.exists() && eof_token) {
      if (map_token_number[eof_token].non_terminal_flag) {
        unsigned charSet = map_token_number[eof_token].token_set_id;
        unsigned *list = (unsigned *) dict_str(char_set_dict,charSet);
        int character = list[1];
        LOGV(charSet);
        LOGV(character);
        token = map_char_number[character].token_number;
      }
      else {
        token = eof_token;
      }
      LOGV(token);
    }
    else {
      token = 0;
    }
  }
  else if (lookAhead != 0) {
    const unsigned char *save = lookAhead;
    token = keyToken();
    lookAhead = save;
    LOGV(token);
    if (token == 0) {
      token = map_char_number[*lookAhead - min_char_number].token_number;
    }
  }
  LOGV(token);
  LOGV(auxStack.size());
  return token;
}
*/

void FtParser::dispatchReductionToken() {
  LOGSECTION("FtParser::dispatchReductionToken");
  unsigned k;
  state_number_map *sp;

  do {
    LOGV(reductionState.number);
    LOGV(reductionState.token);
    sp = &map_state_number[reductionState.number];
    unsigned *tokenPointer = lstptr(*sp,t_actions);
    for (k = sp->n_actions; k && tokenPointer[--k] != reductionState.token;) {
      /* nothing */
    }
    LOGV(k) LCV(tokenPointer[k]);
    assert(tokenPointer[k] == reductionState.token);
    actionParameter = lstptr(*sp,p_actions)[k];
    LOGV(k) LCV(actionParameter);
    if (k == 0) {
      shiftNonterminal();
      processState = selectionError;
      stackChanged(stateStack.size()+1);
      return;
    }
  } while ((this->*(nonterminalAction[lstptr(*sp,a_actions)[k]]))());
  stackChanged(stateStack.size()+1);
}

void FtParser::completeReduction(int token) {
  LOGSECTION("FtParser::completeReduction(int)");
  int k = stateStack.size() - reductionIndex;
  assert((unsigned) k <= (unsigned) stateStack.size());
  LOGV(k);
  stateStack.discardData(k);
  reductionState.token = token;
  LOGV(token);
  processState = running;
  dispatchReductionToken();
  if (processState == running &&
      state.pointer == lookAhead &&
      state.token == 0) {
    getToken();
  }
  nNullShifts = 0;
  auxStack.discardData();
  transStack.discardData();
  LOGV(auxStack.size());
}

void FtParser::completeReduction() {
  LOGSECTION("FtParser::completeReduction()");
  int token = ibnfs[ibnfb[ruleToReduce]+reductionSelection];
  completeReduction(token);
}

void FtParser::parseAction() {
  LOGSECTION("FtParser::parseAction");
  state_number_map *sp = &map_state_number[state.number];
  unsigned *tokenPointer = lstptr(*sp, t_actions);

  if (processState < running) {
    processState = running;
  }
  int k = 0;
  while (tokenPointer[k] != (unsigned) state.token && tokenPointer[k]) {
    k++;
  }

  actionParameter = lstptr(*sp, p_actions)[k];
  ((*this).*( terminalAction[lstptr(*sp, a_actions)[k]] ))();

  if (processState <= running && state.token == 0) {
    getToken();
  }
  stackChanged(stateStack.size()+1);
}

void FtParser::stepToken(unsigned token) {
  LOGSECTION("FtParser::stepToken");
  LOGV(state.number);
  LOGV(state.token) LCV((int) state.pointer);
  lookAhead = state.pointer = 0;
  inputToken = token;
  processState = running;

  if (map_token_number[token].non_terminal_flag) {
    reductionState = state;
    reductionState.token = token;
    state.token = inputToken = 0;
    dispatchReductionToken();
    transStack.discardData();
    auxStack.discardData();
    if (processState <= running && state.token == 0) {
      getToken();
    }
    stackChanged(stateStack.size());
    return;
  }
  inputToken = token;
  state_number_map *sp = &map_state_number[state.number];
  unsigned *tokenPointer = lstptr(*sp, t_actions);
  state.token = token;

  int k = 0;
  while(tokenPointer[k] != (unsigned) state.token && tokenPointer[k]) {
    k++;
  }

  actionParameter = lstptr(*sp, p_actions)[k];
  ((*this).*( terminalAction[lstptr(*sp, a_actions)[k]] ))();
  LOGV(processState) LCV(state.token);
  if (processState <= running && state.token == 0) {
    getToken();
  }
  stackChanged(stateStack.size()+1);
}

void FtParser::parseToken(unsigned token) {
  LOGSECTION("FtParser::parseToken");
  LOGV(state.number) LCV(token);
  LOGV(state.token) LCV((int) state.pointer);
  lookAhead = state.pointer = 0;
  inputToken = token;
  processState = token ? running : syntaxError;
  if (processState == syntaxError) {
    return;
  }

  if (map_token_number[token].non_terminal_flag) {
    reductionState = state;
    reductionState.token = token;
    state.token = inputToken = 0;
    dispatchReductionToken();
    if (processState <= running && state.token == 0) {
      getToken();
    }
    return;
  }
  inputToken = state.token = token;

  while (processState <= running && inputToken != 0) {
    state_number_map *sp = &map_state_number[state.number];
    unsigned *tokenPointer = lstptr(*sp, t_actions);
    int k = 0;
    while(tokenPointer[k] != (unsigned) state.token && tokenPointer[k]) {
      k++;
    }
    LOGV(k) LCV(sp->n_actions);
    assert((unsigned) k < sp->n_actions);
    actionParameter = lstptr(*sp, p_actions)[k];
    ((*this).*( terminalAction[lstptr(*sp, a_actions)[k]] ))();
    LOGV(processState) LCV(state.token);
    if (processState <= running && inputToken != 0) {
      state.token = inputToken;
    }
  }
  if (processState <= running && state.token == 0) {
    getToken();
  }
  stackChanged(stateStack.size()+1);
}



int FtParser::validToken(unsigned token, unsigned sn) {
  LOGSECTION("FtParser::validToken");
  state_number_map *sp = &map_state_number[sn];
  unsigned *tokenPointer = lstptr(*sp, t_actions);

  int k = 0;
  while (tokenPointer[k] && tokenPointer[k] != token && tokenPointer[k]) {
    k++;
  }
  if (k == 0 && lstptr(*sp, a_actions)[k] == pe_syn_error) {
    return 0;
  }
  LOGV(sn);
  LOGV(token);
  LOGV(k);
  LOGV(tokenPointer[k]);
  return 1;
}

int FtParser::validSelection(unsigned selection, unsigned sn) {
  LOGSECTION("FtParser::validSelection");
  state_number_map *sp = &map_state_number[sn];
  unsigned *tokenPointer = lstptr(*sp, t_actions);

  unsigned token = ibnfs[ibnfb[ruleToReduce] + selection];

  int k;
  for (k = sp->n_actions; k && tokenPointer[--k] != token; ) {
    LOGV(k);
    LOGV(tokenPointer[k]);
  }
  LOGV(sn);
  LOGV(token);
  LOGV(k);
  LOGV(tokenPointer[k]);
  return k != 0;
}

FtParser &FtParser::parseTo(unsigned char *target) {
  LOGSECTION("FtParser::parseTo");
  int backup = 0;
  while (stateStack.size() && state.pointer <= target) {
    stateStack.pop(state);
    backup = 1;
  }
  if (backup) {
    processState = running;
    lookAhead = state.pointer;
    auxStack.discardData();
    transStack.discardData();
    LOGV(auxStack.size());
    nNullShifts = 0;
    getToken();
  }
  else if (processState == finished) {
    reset();
  }
  if (processState < running) {
    processState = running;
  }
  LOGV(state.token) LCV((int) state.pointer);
  while (processState <= running && state.pointer < target) {
    parseAction();
  }
  //if ((dc *)displayControl) {
  //  displayControl->des->d_size.y = stateStack.size()+1;
  //}
  stackChanged(stateStack.size()+1);
  return *this;
}

FtParser &FtParser::parse() {
  if (processState < running) {
    processState = running;
  }
  while (processState <= running) {
    parseAction();
  }
  //if ((dc *)displayControl) {
  //  displayControl->des->d_size.y = stateStack.size()+1;
  //}
  stackChanged(stateStack.size()+1);
  return *this;
}

FtParser &FtParser::parse(const char *fragment) {
  LOGSECTION("FtParser::parse(const char *)");
  LOGV(fragment);
  lookAhead = state.pointer = (const unsigned char *) fragment;
  inputToken = 0;
  endPointer = (const unsigned char *) fragment + strlen(fragment);
  getToken();
  LOGV(processState) LCV(inputToken);

  while (processState <= running) {
    parseAction();
    LOGV(state.pointer);
  }
  //if ((dc *)displayControl) {
  //  displayControl->des->d_size.y = stateStack.size()+1;
  //}
  stackChanged(stateStack.size()+1);
  if (processState == unexpectedEndOfFile) {
    processState = ready;
  }
  return *this;
}

FtParser &FtParser::prime(const char *fragment) {
  LOGSECTION("FtParser::parse(const char *)");
  LOGV(fragment);
  lookAhead = state.pointer = (const unsigned char *) fragment;
  endPointer = (const unsigned char *) fragment + strlen(fragment);
  getToken();
  LOGV(processState);
  stackChanged(stateStack.size()+1);
  return *this;
}

FtParser &FtParser::parseTo(cint *loc) {
  LOGSECTION("FtParser::parseTo");
  LOGV(*loc);
  LOGV(stateStack.size());
  LOGV(location());
  int backup = 0;
  LOGV(processState);
  while (stateStack.size()
	 && (loc->y < state.line
	     || (loc->y == state.line && loc->x <= state.charPos))) {
    stateStack.pop(state);
    backup = 1;
  }
  LOGV(*loc) LCV(location());
  LOGV(precedes(*loc, location()));
  if (backup) {
    processState = running;
    lookAhead = state.pointer;
    getToken();
  }
  else if (precedes(*loc, location())) {
    reset();
  }
  LOGV(backup);
  LOGV(processState);
  LOGV(stateStack.size());
  LOGV(state.number) LCV((int) state.pointer);
  LOGV(location());
  if (processState < running) {
    processState = running;
  }
  while (processState == running &&
         (state.line < loc->y || (state.line == loc->y &&
				  state.charPos < loc->x))) {
    parseAction();
  }
  LOGV(location());
  //if ((dc *)displayControl) {
  //  displayControl->des->d_size.y = stateStack.size()+1;
  //}
  stackChanged(stateStack.size()+1);
  return *this;
}

FtParser &FtParser::step() {
  LOGSECTION("FtParser::step");
  LOGV(location());
  if (processState < running) {
    processState = running;
  }
  if (processState == running) {
    parseAction();
  }
  //if ((dc *)displayControl) {
  //  displayControl->des->d_size.y = stateStack.size()+1;
  //}
  stackChanged(stateStack.size()+1);
  return *this;
}

FtParser &FtParser::step(char *fragment) {
  LOGSECTION("FtParser::step(const char *)");
  LOGV(fragment);
  lookAhead = state.pointer = (unsigned char *) fragment;
  endPointer = (unsigned char *) fragment + strlen(fragment);
  getToken();
  LOGV(processState);
  if (processState <= running) {
    parseAction();
  }
  //if ((dc *)displayControl) {
  //  displayControl->des->d_size.y = stateStack.size()+1;
  //}
  stackChanged(stateStack.size()+1);
  if (processState <= running && state.token == 0) {
    getToken();
  }
  if (processState == unexpectedEndOfFile) {
    processState = ready;
  }
  return *this;
}

const char *FtParser::processStateText[] = {
  "Ready",
  "Ready",                                   //running,
  "Parse complete",                          //finished,
  "Syntax error",           //syntaxError,
  "Unexpected end of file", //unexpectedEndOfFile,
  "Select reduction token",  //selectionRequired
  "Selection error"
};

#if 0 /* unused */
tsd *x1x_new(pcb_type *pcb) {
  int sx, sn, tn, /*fn,*/ fx;
  int *items;
  int nitems;
  tsd *isl = init_tsd(4);

  ok_ptr(pcb);
  sn = PCB.s.sn;
  tn = PCB.token_number;
  if (PCB.exit_flag) {
    tn = 0;
  }
  sx = PCB.ssx;
  if (PCB.reduction_token) {
    sx -= PCB.rule_length;
  }

  {
    tuple_dict *d;
    d = xis(sn);
    items = d->text;
    nitems = d->nsx;
    items += 2*nitems;
    while (nitems--) {
      fx = *--items;
      Rule rule = *--items;
      if (tn == 0 ||
          fx >= rule->non_vanishing_length ||
          rule.token(fx).isExpansionToken(tn)) {
	//x2(Rule(fn)->token(fx), tn))
	//x2(lstptr(map_form_number[fn],tokens)[fx], tn))
        at(isl,sx,sn,(int) rule,fx);
      }
    }
    delete_tuple_dict(d);
  }

  while (sx-- > 0) {
    tuple_dict *d;
    tn = map_state_number[sn].char_token;
    sn = PCB.ss[sx].sn;
    d = xis(sn);
    items = d->text;
    nitems = d->nsx;
    items += 2*nitems;
    while (nitems--) {
      fx = *--items;
      Rule rule = *--items;
      if (fx >= rule->length()) {
	continue;
      }
      if (x3(isl, sx, (int)rule, fx)) {
	at(isl, sx, sn, (int) rule, fx);
      }
    }
    delete_tuple_dict(d);
  }
  return isl;
}
#endif /* 0 - unused */

tsd *FtParser::x1x_new() {
  LOGSECTION("FtParser::x1x_new");
  int sx, sn, fn, fx;
  tsd *isl = init_tsd(4);

  sn = state.number;
  Token tn = state.token;
  if (processState == selectionRequired) {
    sn = reductionState.number;
    tn = reductionState.token;
  }
  else if (processState == syntaxError) {
    tn = 0;
  }
  tuple_dict *d = xis(sn);
  int *items = d->text;
  int nitems = d->nsx;
  items += 2*nitems;
  LOGV(state.number);
  LOGV(state.token);
  LOGV(stateStack.size());
  LOGV(processState);
  LOGV(ntkns);
  sx = stateStack.size();
  if (processState == selectionRequired) {
    int rx = Rule(ruleToReduce)->length();
    int n = stateStack.size();
    for (sx = reductionIndex; rx >= 0; rx--) {
      int index = sx + rx;
      if (index > n) {
	continue;
      }
      if (index < n) {
	sn = stateStack[index].number;
      }
      at(isl, index, sn, ruleToReduce, rx);
    }
    tuple_dict *d = xis(reductionState.number);
    int *items = d->text;
    int nitems = d->nsx;
    items += 2*nitems;
    while (nitems--) {
      fx = *--items;
      fn = *--items;
      LOGV(fn);
      LOGV(fx);
      if ((unsigned) fx >= Rule(fn)->length()) {
	continue;
      }
      if (x3a(isl, sx, fn, fx)) {
	at(isl, sx, sn, fn, fx);
      }
    }
  }
  else {
    state_number_map *sp = &map_state_number[sn];
    unsigned *tokenPointer = lstptr(*sp, t_actions);

    unsigned k = 0;
    while (tokenPointer[k] && 
	   tokenPointer[k] != (unsigned) tn && tokenPointer[k]) {
      k++;
    }
    if (k == 0 && lstptr(*sp, a_actions)[k] == pe_syn_error) {
      tn = 0;
    }
    LOGV(k);
    LOGV(tn);
    LOGV(nitems);
    if (tn.isNotNull()) {
      while (nitems--) {
        fx = *--items;
        fn = *--items;
        LOGV(fn);
        LOGV(fx);
        //if (x4(tn, fn)) {
        if (tn.isExpansionRule(fn)) {
          at(isl, sx, sn, fn, fx);
          continue;
        }
        Rule rule = fn;
        if ((unsigned) fx >= rule->length()) {
	  continue;
	}
        //if (lstptr(map_form_number[fn],tokens)[fx] == tn) {
	//  at(isl, sx, sn, fn, fx);
	//}
        if (rule.token(fx) == tn) {
	  at(isl, sx, sn, (int) rule, fx);
	}
      }
      LOGV(isl->nt);
      for (k = 0; k < sp->n_completed_forms; k++) {
        unsigned rule = lstptr(*sp,completed_forms)[k];
        LOGV(rule);
        at(isl, sx, sn, rule, Rule(rule)->length());
      }
      LOGV(isl->nt);
      if (isl->nt) {
        items = d->text;
        nitems = d->nsx;
        items += 2*nitems;
        while (nitems--) {
          fx = *--items;
          fn = *--items;
          LOGV(fn);
          LOGV(fx);
          if ((unsigned) fx >= Rule(fn)->length()) {
	    continue;
	  }
          if (x3a(isl, sx, fn, fx)) {
	    at(isl, sx, sn, fn, fx);
	  }
        }
      }
      else {
        items = d->text;
        nitems = d->nsx;
        items += 2*nitems;
        while (nitems--) {
          fx = *--items;
          fn = *--items;
          LOGV(fn);
          LOGV(fx);
          at(isl, sx, sn, fn, fx);
        }
      }
    }
    else while (nitems--) {
      fx = *--items;
      fn = *--items;
      LOGV(fn);
      LOGV(fx);
      at(isl, sx, sn, fn, fx);
    }
  }
  LOGV(isl->nt);
  LOGV((int) d);
  delete_tuple_dict(d);
  LOGV(sx);
  while (sx-- > 0) {
    tuple_dict *d;
    tn = stateStack[sx].token;
    sn = stateStack[sx].number;
    LOGV(sn);
    LOGV(tn);
    d = xis(sn);
    items = d->text;
    nitems = d->nsx;
    items += 2*nitems;
    while (nitems--) {
      fx = *--items;
      fn = *--items;
      LOGV(fn);
      LOGV(fx);

      if ((unsigned) fx >= Rule(fn)->length()) {
	continue;
      }
      if (x3(isl, sx, fn, fx)) {
	at(isl, sx, sn, fn, fx);
      }
    }
    delete_tuple_dict(d);
  }
  LOGV(isl->nt);
  return isl;
}