#include <iostream>
#include <stack>
#include <array>
#include <map>
#include <string>
#include <vector>
#include <memory>
#include <exception>
class interpreter {
public:
interpreter(std::istream& input, std::ostream& output) : input_(input), output_(output) {};
void load_commands(); // Nacte vsechny instrukce v souboru (streamu). Inicializuje objekt.
void execute_commands(); // Vykona vsechny instrukce.
private:
#pragma region Instruction Classes
class instruction {
public:
virtual void execute(interpreter& interpret) = 0;
};
class ldconst : public instruction {
public:
ldconst(int con) : const_(con) {};
virtual void execute(interpreter& interpret) override {
interpret.stack_.push(const_);
}
private:
int const_;
};
class stloc : public instruction {
public:
stloc(std::size_t local) : local_(local) {};
virtual void execute(interpreter& interpret) override {
interpret.locals_.at(local_) = interpret.stack_.top();
interpret.stack_.pop();
}
private:
std::size_t local_;
};
class ldloc : public instruction {
public:
ldloc(std::size_t local) : local_(local) {};
virtual void execute(interpreter& interpret) override {
interpret.stack_.push(interpret.locals_.at(local_));
}
private:
std::size_t local_;
};
class pop : public instruction {
public:
virtual void execute(interpreter& interpret) override {
interpret.stack_.pop();
}
};
class out : public instruction {
public:
virtual void execute(interpreter& interpret) override {
interpret.output_ << interpret.stack_.top() << " ";
}
};
class add : public instruction {
public:
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.size() < 2) throw std::runtime_error("Too few operands on the stack.");
int right_op = interpret.stack_.top(); interpret.stack_.pop();
int left_op = interpret.stack_.top(); interpret.stack_.pop();
interpret.stack_.push(left_op + right_op);
}
};
class sub : public instruction {
public:
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.size() < 2) throw std::runtime_error("Too few operands on the stack.");
int right_op = interpret.stack_.top(); interpret.stack_.pop();
int left_op = interpret.stack_.top(); interpret.stack_.pop();
interpret.stack_.push(left_op - right_op);
}
};
class mul : public instruction {
public:
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.size() < 2) throw std::runtime_error("Too few operands on the stack.");
int right_op = interpret.stack_.top(); interpret.stack_.pop();
int left_op = interpret.stack_.top(); interpret.stack_.pop();
interpret.stack_.push(left_op * right_op);
}
};
class div : public instruction {
public:
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.size() < 2) throw std::runtime_error("Too few operands on the stack.");
int right_op = interpret.stack_.top(); interpret.stack_.pop();
int left_op = interpret.stack_.top(); interpret.stack_.pop();
interpret.stack_.push(left_op / right_op);
}
};
class lt : public instruction {
public:
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.size() < 2) throw std::runtime_error("Too few operands on the stack.");
int right_op = interpret.stack_.top(); interpret.stack_.pop();
int left_op = interpret.stack_.top(); interpret.stack_.pop();
interpret.stack_.push(left_op < right_op ? 1 : 0);
}
};
class gt : public instruction {
public:
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.size() < 2) throw std::runtime_error("Too few operands on the stack.");
int right_op = interpret.stack_.top(); interpret.stack_.pop();
int left_op = interpret.stack_.top(); interpret.stack_.pop();
interpret.stack_.push(left_op > right_op ? 1 : 0);
}
};
class gotoi : public instruction {
public:
gotoi(const std::string& label) : label_(label + ":") {};
virtual void execute(interpreter& interpret) override {
auto inst = interpret.labels_.find(label_);
if (inst == interpret.labels_.end())
throw std::runtime_error("The label " + label_ + " has not been defined.");
else
interpret.current_instruction_ = inst->second;
}
private:
std::string label_;
};
class brt : public instruction {
public:
brt(const std::string& label) : label_(label + ":") {};
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.top() != 0) {
auto inst = interpret.labels_.find(label_);
if (inst == interpret.labels_.cend())
throw std::runtime_error("The label " + label_ + "has not been defined.");
else
interpret.current_instruction_ = inst->second;
}
}
private:
std::string label_;
};
class brf : public instruction {
public:
brf(const std::string& label) : label_(label + ":") {};
virtual void execute(interpreter& interpret) override {
if (interpret.stack_.top() == 0) {
auto inst = interpret.labels_.find(label_);
if (inst == interpret.labels_.cend())
throw std::runtime_error("The label " + label_ + "has not been defined.");
else
interpret.current_instruction_ = inst->second;
}
}
private:
std::string label_;
};
#pragma endregion
std::vector<std::string> get_tokens_(const std::string& line); // Rozdeli radku na jednotlive tokeny.
void process_line_(const std::string& line); // Zpracuje jednu radku na vstupu dle zadanych pravidel.
std::size_t current_instruction_; // Index prave vykonavane instrukce (viz. nasledujici vektor).
std::vector<std::unique_ptr<instruction>> instructions_; // Zde je ulozena posloupnost vsech instrukci.
std::map<std::string, std::size_t> labels_; // Prvni je jmeno navesti, druhy je cislo radku (instrukce) v kodu (viz. predchozi vektor), indexace od nuly.
std::array<int, 10> locals_; // Lokalni promenne.
std::stack<int> stack_; // Vyhodnocovaci zasobnik.
std::istream& input_;
std::ostream& output_;
};