L_Interpreter.h

#pragma once
#ifndef L_INTERPRETER_H
#define L_INTERPRETER_H

#include <string>
#include <vector>

// Interprets a file written in the L language devised by Martin Davis
// This language has only three instructions:
//
// 1. Increment: X <- X + 1
// 2. Decrement: X <- X - 1
// 3. Conditional GOTO: IF X != 0 GOTO LabelName
//
// The inputs X1, X2, X3 ... XN are passed into the interpreter as a vector of integers.
// These input variables can be any positive numbers.
// Additional rules of the language:
//
// These input variables can be any positive numbers. If decrementing would make a variable negative,
// instead that variable is set to 0.
// Local variables can be used, and are initialized as 0. 
// The output variable can be used, and is initialized as 0.
// If a GOTO would go to a label that does not exist, the program terminates.

int L_InterpretFile(std::string filename, std::vector args);

#endif

L_Interpreter.cpp

#include "L_Interpreter.h"
#include <regex>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <map>

// All valid instructions will match one of three regular expressions
const std::regex L_Increment_Regex("(.+) <- (.+) \\+ 1");
const std::regex L_Decrement_Regex("(.+) <- (.+) \\- 1");
const std::regex L_Conditional_Regex("IF (.+) != 0 GOTO (.+)");

// Helper function: get the nth token of a string
std::string getToken(std::string str, int n) {
	std::stringstream ss(str);
	std::string token;
	for (int i = -1; i < n; i++) { ss >> token; }
	return token;
}

int L_InterpretFile(std::string filename, std::vector args) {

	// Initialize variables as 0, and create a blank list of instructions
	std::vector instruction_list;
	std::map variables;
	std::map labels;
	for (unsigned int i = 0; i < args.size(); i++) {  variables["X" + std::to_string(i + 1)] = std::max(0, args.at(i)); }
	variables["Y"] = 0;

	// Try opening the file, and if this fails, return 0
	std::ifstream fp(filename);
	if (!fp) { 
		std::cout << "Error - Could not open file: " << filename << std::endl;
		return 0;
	}

	// Load the contents of the file one line at a time
	int at_line = 0;
	std::string next_instruction;
	while (std::getline(fp, next_instruction)) {

		// Get the line number of any label that is come across, then remove the label
		std::string first_token = getToken(next_instruction, 0);
		if (first_token.at(0) == '[' && first_token.at(first_token.size() - 1 == ']')) {
			labels[first_token.substr(1, first_token.size() - 2)] = at_line;
			next_instruction = next_instruction.substr(first_token.size() + 1, next_instruction.size() - 1);
		}
		at_line++;

		// If a line is a valid instruction, insert it to the instruction list
		if (std::regex_match(next_instruction, L_Increment_Regex) || std::regex_match(next_instruction, L_Decrement_Regex)) {
			if (getToken(next_instruction, 0).compare(getToken(next_instruction, 2))) {
				std::cout << "Error - Poorly formatted instruction: " << next_instruction << std::endl;
			}
			else {
				if (!variables.count(getToken(next_instruction, 0))) { variables[getToken(next_instruction, 0)] = 0; }
				instruction_list.push_back(next_instruction);
			}
		}
		else if (std::regex_match(next_instruction, L_Conditional_Regex)) {
			if (!variables.count(getToken(next_instruction, 1))) { variables[getToken(next_instruction, 1)] = 0; }
			instruction_list.push_back(next_instruction);
		}
		else { std::cout << "Error - Poorly formatted instruction: " << next_instruction << std::endl; }
	}

	// Execute the instruction list
	int cursor = 0;
	while (cursor != instruction_list.size()) {
		// Increment instruction
		if (std::regex_match(instruction_list.at(cursor), L_Increment_Regex)) {
			variables[getToken(instruction_list.at(cursor), 0)]++;
			cursor++;
		}
		// Decrement instruction
		else if (std::regex_match(instruction_list.at(cursor), L_Decrement_Regex)) {
			variables[getToken(instruction_list.at(cursor), 0)] = std::max(variables[getToken(instruction_list.at(cursor), 0)]-1, 0);
			cursor++;
		}
		// Conditional instruction
		else if (std::regex_match(instruction_list.at(cursor), L_Conditional_Regex)) {
			if (variables[getToken(instruction_list.at(cursor), 1)] != 0) {
				std::string label_name = getToken(instruction_list.at(cursor), 5);
				if (labels.count(label_name)) { cursor = labels[label_name]; }
				else { return variables["Y"]; }
			} else { cursor++; }
		}
		// Invalid command
		else { cursor++; }
	}

	// Return the output
	return variables["Y"];
}