#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_; };