view interps/rail/src/interactive.cpp @ 12493:885661512b17 draft

<int-e> le//rn schwartzian//In 1987, Yogurt introduced a better way to rank Schwartz users: Rather than holding an annual tournament, users would take a series of standardized tests adminstered by official Schwartz centers, and would then be ranked according to the results. This lead to the Schwartzian transform because it allowed many more users to be ranked.
author HackEso <hackeso@esolangs.org>
date Fri, 12 Jan 2024 07:24:55 +0000
parents 859f9b4339e6
children
line wrap: on
line source

// interactive.cpp

#include "lib.h"
#include "interactive.h"
#include "Thread.h"
#include "Board.h"
#include "Vec.h"
#include "Binding.h"
#include "ActivationRecord.h"

using namespace std;

// In theory, these should all be mutually exclusive.

#ifdef _WIN32

#include "windows.h"

void redisplay(Thread & program);
void gotoPosition(int x, int y);
void printString(string const & line);
void setColor(WORD bits);
char readChar(void);
string getLastErrorString(void);
string convertToDebugString(string const & line);

HANDLE inputHandle;
HANDLE outputHandle;

static const WORD FOREGROUND_WHITE = FOREGROUND_BLUE | FOREGROUND_GREEN
                                     | FOREGROUND_RED;

static const WORD BACKGROUND_WHITE = BACKGROUND_BLUE | BACKGROUND_GREEN
                                     | BACKGROUND_RED;

void runInteractive(Thread & program)
{
  inputHandle = GetStdHandle(STD_INPUT_HANDLE);
  outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  SetConsoleMode(inputHandle, ENABLE_PROCESSED_INPUT);
  SetConsoleMode(outputHandle, ENABLE_PROCESSED_OUTPUT);
  COORD bufferSize;
  bufferSize.X = 80;
  bufferSize.Y = 25;
  SetConsoleScreenBufferSize(outputHandle, bufferSize);

  char choice = '\0';
  bool doRead = true;
  while (program.getStatus() == Error::runnable)
  {
    redisplay(program);
    if (doRead)
    {
      choice = readChar();
      if (choice == 's')
      {
        program.step();
      }
      else if (choice == 'r')
      {
        doRead = false;
      }
      else if (choice == 'x')
      {
        break;
      }
      else
      {
        // Do nothing.
      }
    }
    else
    {
      program.step();
    }
  }
}

void redisplay(Thread & program)
{
  list<Binding> const & dataStack(program.getDataStack());
  list<ActivationRecord> const & programStack(program.getProgramStack());
  ActivationRecord const & currentRecord(programStack.front());
  Board const * currentBoard(currentRecord.getFunction());
  if (currentBoard == NULL)
  {
    throw InternalException("redisplay(): currentBoard is NULL");
  }
  Vec currentPos = currentRecord.getPosition();
  // We want the current position in the center. Therefore we subtract
  // this offset from all position calculations in the program world.
  static const int windowOffset = 11;
  int y = 0;
  string line;
  line.reserve(35);

  // Print window on the board.
  setColor(FOREGROUND_WHITE);
  for (y = 0; y < 23; ++y)
  {
    line.clear();
    for (int x = 0; x < 23; ++ x)
    {
      line.push_back(currentBoard->at(Vec(x + currentPos.x - windowOffset,
                                          y + currentPos.y - windowOffset)));
    }
    gotoPosition(0, y);
    printString(line);
  }

  // Print current position in a different color for emphasis.
  gotoPosition(11,11);
  setColor(FOREGROUND_WHITE | BACKGROUND_BLUE);
  line.clear();
  line.push_back(currentBoard->at(currentPos));
  printString(line);

  // Print program stack
  setColor(FOREGROUND_WHITE);
  gotoPosition(24, 0);
  printString("Program Stack (top)");
  gotoPosition(24, 1);
  printString("-------------------");
  list<ActivationRecord>::const_iterator programPos = programStack.begin();
  list<ActivationRecord>::const_iterator programLimit = programStack.end();
  if (programStack.size() >= 21)
  {
    y = 2;
  }
  else
  {
    y = 23 - static_cast<int>(programStack.size());
  }
  //                                            1
  //                                  0123456789012345678
  static const string programBlank = "                   ";
  int blankY = 0;
  for (blankY = 2; blankY < y; ++blankY)
  {
    gotoPosition(24, blankY);
    printString(programBlank);
  }
  for (; programPos != programLimit && y < 23; ++programPos, ++y)
  {
    gotoPosition(24, y);
    line = programPos->getFunction()->getName();
    if (line.size() > 19)
    {
      line.resize(19);
    }
    while (line.size() < 19)
    {
      line.push_back(' ');
    }
    printString(line);
  }

  // Print data stack
  if (dataStack.size() >= 21)
  {
    y = 2;
  }
  else
  {
    y = 23 - static_cast<int>(dataStack.size());
  }
  static const string dataBlank =
  //           1         2         3         4
  // 012345678901234567890123456789012345678901234
    "                                             ";
  for (blankY = 0; blankY < y; ++blankY)
  {
    gotoPosition(44, blankY);
    printString(dataBlank);
  }
  gotoPosition(44, 0);
  printString("Data Stack (top)");
  gotoPosition(44, 1);
  printString("----------------");
  list<Binding>::const_iterator dataPos = dataStack.begin();
  list<Binding>::const_iterator dataLimit = dataStack.end();
  for (; dataPos != dataLimit && y < 23; ++dataPos, ++y)
  {
    gotoPosition(44, y);
    line = convertToDebugString((*dataPos)->toString());
    if (line.size() > 36)
    {
      line.resize(36);
    }
    while (line.size() < 36)
    {
      line.push_back(' ');
    }
    printString(line);
  }

  // Print blank columns
  setColor(FOREGROUND_WHITE | BACKGROUND_GREEN);
  static const string blankCol = " ";
  for (y = 0; y < 23; ++y)
  {
    gotoPosition(23, y);
    printString(blankCol);
    gotoPosition(43, y);
    printString(blankCol);
  }
  for (int x = 0; x < 80; ++x)
  {
    gotoPosition(x, 23);
    printString(blankCol);
  }
  gotoPosition(0, 23);
  printString("(" + intToString(currentPos.x) + ", "
              + intToString(currentPos.y) + ") "
              + Dir::dirToString(currentRecord.getDirection()));

  setColor(FOREGROUND_WHITE | BACKGROUND_RED);
  for (int x = 0; x < 80; ++x)
  {
    gotoPosition(x, 24);
    printString(blankCol);
  }
  gotoPosition(0, 24);
  printString("Please (s)tep, (r)un, or e(x)it");
  setColor(FOREGROUND_WHITE);
}

void gotoPosition(int x, int y)
{
  COORD pos;
  pos.X = x;
  pos.Y = y;
  if (SetConsoleCursorPosition(outputHandle, pos) == 0)
  {
    throw InternalException("redisplay(): Failed SetConsoleCursorPosition ("
                            + intToString(pos.X) + ", " + intToString(pos.Y)
                            + "): " + getLastErrorString());
  }
}

void printString(string const & line)
{
  DWORD writeCount = 0;
  BOOL error = WriteConsole(outputHandle, line.c_str(), static_cast<DWORD>(line.size()),
                            &writeCount, NULL);
  if (error == 0)
  {
    throw InternalException("printString(): Failed WriteConsole (" + line
                            + "): " + getLastErrorString());
  }
  if (writeCount < line.size())
  {
    throw InternalException(
      "printString(): WriteConsole didn't write enough characters (" + line
      + ")");
  }
}

void setColor(WORD bits)
{
  if (SetConsoleTextAttribute(outputHandle, bits) == 0)
  {
    throw InternalException("setColor(): Failed SetConsoleTextAttribute(): "
                            + getLastErrorString());
  }
}

char readChar(void)
{
  char buffer[2];
  DWORD readCount = 0;
  if (ReadConsole(inputHandle, buffer, 1, &readCount, NULL) == 0)
  {
    throw InternalException("readChar(): Failed ReadConsole(): "
                            + getLastErrorString());
  }
  if (readCount < 1)
  {
    throw InternalException("readChar(): ReadConsole didn't read any characters.");
  }
  return buffer[0];
}

string getLastErrorString(void)
{
  char * buffer = NULL;
  DWORD errorNumber = GetLastError();
  string error = "Error #" + intToString(errorNumber) + ": ";
  if (!FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        errorNumber,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &buffer,
        0,
        NULL ))
  {
    error += "META ERROR! An error occurred while trying to get the text for GetLastError()";
  }
  else
  {
    error += buffer;
  }
  LocalFree(buffer);
  return error;
}

string convertToDebugString(string const & line)
{
  string result;
  result.reserve(line.size());
  for (size_t i = 0; i < line.size(); ++i)
  {
    if (line[i] == '\\')
    {
      result += "\\\\";
    }
    else if (line[i] == '\n')
    {
      result += "\\n\\";
    }
    else if (line[i] == '\t')
    {
      result += "\\t\\";
    }
    else if (line[i] == '[')
    {
      result += "\\[\\";
    }
    else if (line[i] == ']')
    {
      result += "\\]\\";
    }
    else
    {
      result += line[i];
    }
  }
  return result;
}

#endif

#ifdef __GNUC__

void runInteractive(Thread & program)
{
  throw InternalException("Interactive mode is not yet implemented for *nix.");
}

#endif