view interps/rail/src/main.cpp @ 12518:2d8fe55c6e65 draft default tip

<int-e> learn The password of the month is release incident pilot.
author HackEso <hackeso@esolangs.org>
date Sun, 03 Nov 2024 00:31:02 +0000
parents 859f9b4339e6
children
line wrap: on
line source

// main.cpp

#include "lib.h"
#include "Thread.h"
#include "Board.h"
#include "interactive.h"

using namespace std;

void parseAllArguments(int argc, char * argv[]);
void parseArgument(string const & arg);
bool hasPrefix(string const & subject, string const & prefix);
void parseInput(map<string, Board> & globals);
void parseFile(ifstream & file, map<string, Board> & globals);
void parseField(ifstream & file, list<string> & field, string & line);
string parseHeader(string const & line);

int main(int argc, char * argv[])
{
  int result = 0;
  try
  {
    parseAllArguments(argc, argv);
    map<string, Board> globals;
    parseInput(globals);

    Thread program;
    map<string, Board>::iterator mainPos = globals.find("main");
    if (mainPos != globals.end())
    {
      program.reset(mainPos->second, &globals);
    }
    else
    {
      throw InternalException("Crash: No 'main' function found");
    }
    if (args::isBatch)
    {
      program.go();
    }
    else
    {
      runInteractive(program);
    }
  }
  catch(ArgumentException & error)
  {
    cerr << "Argument Error: " << error.what() << endl;
    cerr << "Usage: " << argv[0] << " [--batch] [--interactive] " << endl
         << "        [--input=<file>] [--output=<file>] <sourcefile> " << endl
         << "        [<sourcefile> [...]]" << endl << endl;

    cerr << "--batch           Runs in batch mode. The interpreted program will run" << endl
         << "                  silently to completion." << endl;

    cerr << "--interactive     Runs in interactive mode. A step by step readout of the" << endl
         << "                  interpreted program is displayed with debugging controls." << endl
         << "                  If the run is interactive, then input and output must both"
         << "                  be bound to files." << endl;

    cerr << "--input=<file>    Sets standard input of the interpreted program to <file>." << endl
         << "                  Default is the outer program's standard input." << endl;

    cerr << "--ouput=<file>    Sets standard output of the interpreted program to <file>" << endl
         << "                  Default is the outer program's standard output." << endl;

    result = 2;
  }
  catch(std::exception & error)
  {
    cerr << "Internal Error: " << error.what() << endl;
    result = 1;
  }
  return result;
}

void parseAllArguments(int argc, char * argv[])
{
  for (int i = 1; i < argc; ++i)
  {
    parseArgument(argv[i]);
  }
  if (!(args::isBatch) && (args::output == NULL || args::input == NULL))
  {
    throw ArgumentException("If the run is interactive, then input and output must both be bound to files.");
  }
  else if (args::sourceFiles.empty())
  {
    throw ArgumentException("No source files to interpret.");
  }

  if (args::output == NULL)
  {
    args::output = &cout;
  }
  if (args::input == NULL)
  {
    args::input = &cin;
  }
}

void parseArgument(string const & arg)
{
  static const string batchFlag = "--batch";
  static const string interactiveFlag = "--interactive";
  static const string inputPrefix = "--input=";
  static const string outputPrefix = "--output=";

  static ifstream input;
  static ofstream output;
  if (arg == batchFlag)
  {
    args::isBatch = true;
  }
  else if (arg == interactiveFlag)
  {
    args::isBatch = false;
  }
  else if (hasPrefix(arg, inputPrefix))
  {
    if (args::input == NULL)
    {
      input.open(arg.substr(inputPrefix.size()).c_str(), ios::in);
      if (!input)
      {
        throw ArgumentException("Failed to open '--input' file: "
                                + arg.substr(inputPrefix.size()));
      }
      args::input = &input;
    }
    else
    {
      throw ArgumentException("Only one '--input' file is allowed: " + arg);
    }
  }
  else if (hasPrefix(arg, outputPrefix))
  {
    if (args::output == NULL)
    {
      output.open(arg.substr(outputPrefix.size()).c_str(),
                  ios::out | ios::trunc);
      if (!output)
      {
        throw ArgumentException("Failed to open '--output' file: "
                                + arg.substr(outputPrefix.size()));
      }
      args::output = &output;
    }
    else
    {
      throw ArgumentException("Only one '--output' file is allowed: " + arg);
    }
  }
  else
  {
    // This must be a source filename.
    args::sourceFiles.push_back(arg);
  }
}

bool hasPrefix(string const & subject, string const & prefix)
{
  return subject.compare(0, prefix.size(), prefix) == 0;
}

void parseInput(map<string, Board> & globals)
{
  list<string>::iterator pos = args::sourceFiles.begin();
  list<string>::iterator limit = args::sourceFiles.end();
  for (; pos != limit; ++pos)
  {
    ifstream source(pos->c_str(), ios::in);
    if (!source)
    {
      throw ArgumentException("Failed to open source file: " + *pos);
    }
    parseFile(source, globals);
  }
}

void parseFile(ifstream & file, map<string, Board> & globals)
{
  list<string> field;
  string line;
  string name;
  parseField(file, field, line);
  while (file)
  {
    field.clear();
    field.push_back(line);
    name = parseHeader(line);
    parseField(file, field, line);
    if (name != "")
    {
      globals[name].reset(name, field);
    }
  }
}

void parseField(ifstream & file, list<string> & field, string & line)
{
  getline(file, line);
  while (file && line[0] != '$')
  {
    field.push_back(line);
    getline(file, line);
  }
}

string parseHeader(string const & line)
{
  string result;
  size_t first = line.find('\'');
  if (first != string::npos)
  {
    size_t second = line.find('\'', first + 1);
    if (second != string::npos)
    {
      result = line.substr(first + 1, second - (first + 1));
    }
  }
  return result;
}