Compare commits
4 Commits
207c387001
...
f349442410
Author | SHA1 | Date |
---|---|---|
Marco Cetica | f349442410 | |
Marco Cetica | d0ffb8610a | |
Marco Cetica | b9235470e3 | |
Marco Cetica | 505ed8b148 |
|
@ -1,7 +1,7 @@
|
|||
name: dc
|
||||
on:
|
||||
push:
|
||||
branches: [master, unit_testing]
|
||||
branches: [master, factory_parsing]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
|
473
src/eval.cpp
473
src/eval.cpp
|
@ -17,245 +17,69 @@
|
|||
(VAL.at(0) == ':' || VAL.at(0) == ';'))
|
||||
|
||||
#define X_CONTAINS_Y(X, Y) ((Y.find_first_of(X) != std::string::npos))
|
||||
#define MAKE_UNIQUE_PTR(CLASS, ARG) [](){ return std::make_unique<CLASS>(ARG); }
|
||||
|
||||
void Evaluate::init_environment() {
|
||||
// Numerical operations
|
||||
this->op_factory.emplace("+", MAKE_UNIQUE_PTR(Math, OPType::ADD));
|
||||
this->op_factory.emplace("-", MAKE_UNIQUE_PTR(Math, OPType::SUB));
|
||||
this->op_factory.emplace("*", MAKE_UNIQUE_PTR(Math, OPType::MUL));
|
||||
this->op_factory.emplace("/", MAKE_UNIQUE_PTR(Math, OPType::DIV));
|
||||
this->op_factory.emplace("%", MAKE_UNIQUE_PTR(Math, OPType::MOD));
|
||||
this->op_factory.emplace("~", MAKE_UNIQUE_PTR(Math, OPType::DIV_MOD));
|
||||
this->op_factory.emplace("|", MAKE_UNIQUE_PTR(Math, OPType::MOD_EXP));
|
||||
this->op_factory.emplace("^", MAKE_UNIQUE_PTR(Math, OPType::EXP));
|
||||
this->op_factory.emplace("v", MAKE_UNIQUE_PTR(Math, OPType::SQRT));
|
||||
this->op_factory.emplace("sin", MAKE_UNIQUE_PTR(Math, OPType::SIN));
|
||||
this->op_factory.emplace("cos", MAKE_UNIQUE_PTR(Math, OPType::COS));
|
||||
this->op_factory.emplace("tan", MAKE_UNIQUE_PTR(Math, OPType::TAN));
|
||||
this->op_factory.emplace("asin", MAKE_UNIQUE_PTR(Math, OPType::ASIN));
|
||||
this->op_factory.emplace("acos", MAKE_UNIQUE_PTR(Math, OPType::ACOS));
|
||||
this->op_factory.emplace("atan", MAKE_UNIQUE_PTR(Math, OPType::ATAN));
|
||||
this->op_factory.emplace("!", MAKE_UNIQUE_PTR(Math, OPType::FACT));
|
||||
this->op_factory.emplace("pi", MAKE_UNIQUE_PTR(Math, OPType::PI));
|
||||
this->op_factory.emplace("e", MAKE_UNIQUE_PTR(Math, OPType::E));
|
||||
// Stack operations
|
||||
this->op_factory.emplace("p", MAKE_UNIQUE_PTR(Stack, OPType::PCG));
|
||||
this->op_factory.emplace("pb", MAKE_UNIQUE_PTR(Stack, OPType::PBB));
|
||||
this->op_factory.emplace("ph", MAKE_UNIQUE_PTR(Stack, OPType::PBH));
|
||||
this->op_factory.emplace("po", MAKE_UNIQUE_PTR(Stack, OPType::PBO));
|
||||
this->op_factory.emplace("P", MAKE_UNIQUE_PTR(Stack, OPType::P));
|
||||
this->op_factory.emplace("c", MAKE_UNIQUE_PTR(Stack, OPType::CLR));
|
||||
this->op_factory.emplace("R", MAKE_UNIQUE_PTR(Stack, OPType::PH));
|
||||
this->op_factory.emplace("r", MAKE_UNIQUE_PTR(Stack, OPType::SO));
|
||||
this->op_factory.emplace("d", MAKE_UNIQUE_PTR(Stack, OPType::DP));
|
||||
this->op_factory.emplace("f", MAKE_UNIQUE_PTR(Stack, OPType::PS));
|
||||
this->op_factory.emplace("Z", MAKE_UNIQUE_PTR(Stack, OPType::CH));
|
||||
this->op_factory.emplace("z", MAKE_UNIQUE_PTR(Stack, OPType::CS));
|
||||
this->op_factory.emplace("k", MAKE_UNIQUE_PTR(Stack, OPType::SP));
|
||||
this->op_factory.emplace("K", MAKE_UNIQUE_PTR(Stack, OPType::GP));
|
||||
this->op_factory.emplace("o", MAKE_UNIQUE_PTR(Stack, OPType::SOR));
|
||||
this->op_factory.emplace("O", MAKE_UNIQUE_PTR(Stack, OPType::GOR));
|
||||
this->op_factory.emplace("i", MAKE_UNIQUE_PTR(Stack, OPType::SIR));
|
||||
this->op_factory.emplace("I", MAKE_UNIQUE_PTR(Stack, OPType::GIR));
|
||||
// Macro operations
|
||||
this->op_factory.emplace("x", MAKE_UNIQUE_PTR(Macro, OPType::EX));
|
||||
this->op_factory.emplace("?", MAKE_UNIQUE_PTR(Macro, OPType::RI));
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::eval() {
|
||||
// Set up environment
|
||||
init_environment();
|
||||
|
||||
for(size_t idx = 0; idx < this->expr.size(); idx++) {
|
||||
auto val = this->expr.at(idx);
|
||||
std::optional<std::string> err;
|
||||
|
||||
//
|
||||
// NUMERICAL OPERATIONS
|
||||
//
|
||||
if(val == "+") {
|
||||
auto math = std::make_unique<Math>(OPType::ADD, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
auto token = this->expr.at(idx);
|
||||
|
||||
// If token exists in the environment, instantiate it through the factory
|
||||
if (this->op_factory.find(token) != this->op_factory.end()) {
|
||||
std::unique_ptr<IOperation> operation = this->op_factory[token]();
|
||||
err = operation->exec(this->stack, this->parameters, this->regs);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "-") {
|
||||
auto math = std::make_unique<Math>(OPType::SUB, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "*") {
|
||||
auto math = std::make_unique<Math>(OPType::MUL, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "/") {
|
||||
auto math = std::make_unique<Math>(OPType::DIV, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "%") {
|
||||
auto math = std::make_unique<Math>(OPType::MOD, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "~") {
|
||||
auto math = std::make_unique<Math>(OPType::DIV_MOD, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "|") {
|
||||
auto math = std::make_unique<Math>(OPType::MOD_EXP, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "^") {
|
||||
auto math = std::make_unique<Math>(OPType::EXP, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "v") {
|
||||
auto math = std::make_unique<Math>(OPType::SQRT, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "sin") {
|
||||
auto math = std::make_unique<Math>(OPType::SIN, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "cos") {
|
||||
auto math = std::make_unique<Math>(OPType::COS, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "tan") {
|
||||
auto math = std::make_unique<Math>(OPType::TAN, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "asin") {
|
||||
auto math = std::make_unique<Math>(OPType::ASIN, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "acos") {
|
||||
auto math = std::make_unique<Math>(OPType::ACOS, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "atan") {
|
||||
auto math = std::make_unique<Math>(OPType::ATAN, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "!") {
|
||||
auto math = std::make_unique<Math>(OPType::FACT, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "pi") {
|
||||
auto math = std::make_unique<Math>(OPType::PI, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "e") {
|
||||
auto math = std::make_unique<Math>(OPType::E, this->parameters.precision);
|
||||
err = math->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
//
|
||||
// STACK OPERATIONS
|
||||
//
|
||||
else if(val == "p") { // PRINT TOP ELEMENT OF STACK
|
||||
auto op = std::make_unique<Stack>(OPType::PCG, this->parameters.oradix);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "pb") { // PRINT TOP ELEMENT IN BASE 2
|
||||
auto op = std::make_unique<Stack>(OPType::PBB, this->parameters.oradix);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "ph") { // PRINT TOP ELEMENT IN BASE 16
|
||||
auto op = std::make_unique<Stack>(OPType::PBH, this->parameters.oradix);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "po") { // PRINT TOP ELEMENT IN BASE 8
|
||||
auto op = std::make_unique<Stack>(OPType::PBO, this->parameters.oradix);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "P") { // PRINT TOP ELEMENT WITHOUT NEWLINE
|
||||
auto op = std::make_unique<Stack>(OPType::P, this->parameters.oradix);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "c") { // CLEAR THE STACK
|
||||
auto op = std::make_unique<Stack>(OPType::CLR);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "R") { // POP HEAD OF THE STACK WITHOUT PRINTING IT
|
||||
auto op = std::make_unique<Stack>(OPType::PH);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "r") { // SWAP ORDER OF THE TWO TOP ELEMENTS
|
||||
auto op = std::make_unique<Stack>(OPType::SO);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "d") { // DUPLICATE THE HEAD OF THE STACK
|
||||
auto op = std::make_unique<Stack>(OPType::DP);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "f") { // PRINT THE WHOLE STACK
|
||||
auto op = std::make_unique<Stack>(OPType::PS, this->parameters.oradix);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "Z") { // COMPUTE HEAD SIZE(NUM. OF CHARS/DIGITS)
|
||||
auto op = std::make_unique<Stack>(OPType::CH);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "z") { // COMPUTE STACK SIZE
|
||||
auto op = std::make_unique<Stack>(OPType::CS);
|
||||
err = op->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "k") { // SET PRECISION
|
||||
err = fn_set_precision();
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "K") { // GET PRECISION
|
||||
err = fn_get_precision();
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "o") { // SET OUTPUT BASE
|
||||
err = fn_set_oradix();
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "O") { // GET OUTPUT BASE
|
||||
err = fn_get_oradix();
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "i") { // SET INPUT BASE
|
||||
err = fn_set_iradix();
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "I") { // GET INPUT BASE
|
||||
err = fn_get_iradix();
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "x") { // EXECUTE MACRO
|
||||
auto macro = std::make_unique<Macro>(OPType::EX, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "?") { // READ LINE FROM STDIN
|
||||
auto macro = std::make_unique<Macro>(OPType::RI, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(val == "q") { // QUIT GRACEFULLY
|
||||
std::exit(0);
|
||||
} else {
|
||||
err = handle_special(val, idx);
|
||||
err = handle_special(token, idx);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
|
@ -265,21 +89,23 @@ std::optional<std::string> Evaluate::eval() {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::handle_special(std::string val, size_t &idx) {
|
||||
std::optional<std::string> Evaluate::handle_special(std::string token, size_t &idx) {
|
||||
std::optional<std::string> err = std::nullopt;
|
||||
|
||||
if(MACRO_COND(val)) {
|
||||
if(MACRO_COND(token)) {
|
||||
err = parse_macro(idx);
|
||||
} else if(MACRO_CMD_COND(val)) {
|
||||
err = parse_macro_command(val);
|
||||
} else if(REGISTER_COND(val)) {
|
||||
err = parse_register_command(val);
|
||||
} else if(ARRAY_COND(val)) {
|
||||
err = parse_array_command(val);
|
||||
} else if(MACRO_CMD_COND(token)) {
|
||||
err = parse_macro_command(token);
|
||||
} else if(REGISTER_COND(token)) {
|
||||
err = parse_register_command(token);
|
||||
} else if(ARRAY_COND(token)) {
|
||||
err = parse_array_command(token);
|
||||
} else if(this->parameters.iradix != 10) {
|
||||
err = parse_base_n(val);
|
||||
} else if(is_num<double>(val)) {
|
||||
this->stack.push_back(val);
|
||||
err = parse_base_n(token);
|
||||
} else if(is_num<double>(token)) {
|
||||
this->stack.push_back(token);
|
||||
} else if(token == "q") {
|
||||
std::exit(0);
|
||||
} else {
|
||||
return "Unrecognized option";
|
||||
}
|
||||
|
@ -287,15 +113,15 @@ std::optional<std::string> Evaluate::handle_special(std::string val, size_t &idx
|
|||
return err;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::parse_base_n(const std::string& val) {
|
||||
std::optional<std::string> Evaluate::parse_base_n(const std::string& token) {
|
||||
// Discard values that are neither integers neither within "ABCDEF"
|
||||
if(!is_num<long>(val) && !X_CONTAINS_Y("ABCDEF", val)) {
|
||||
if(!is_num<long>(token) && !X_CONTAINS_Y("ABCDEF", token)) {
|
||||
return "This input base supports integers only";
|
||||
}
|
||||
|
||||
// Try to convert the number to the selected numeric base
|
||||
try {
|
||||
long number = std::stol(val, nullptr, this->parameters.iradix);
|
||||
long number = std::stol(token, nullptr, this->parameters.iradix);
|
||||
this->stack.push_back(std::to_string(number));
|
||||
} catch(...) {
|
||||
return "Invalid number for input base '"
|
||||
|
@ -350,7 +176,7 @@ std::optional<std::string> Evaluate::parse_macro(size_t &idx) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::parse_macro_command(std::string val) {
|
||||
std::optional<std::string> Evaluate::parse_macro_command(std::string token) {
|
||||
// A macro command is a comparison symbol(>, <, =, >=, <=, !=)
|
||||
// followed by a register name(e.g, >A)
|
||||
// If command has length equal to three, then it's either '<=', '>=' or '!='
|
||||
|
@ -358,52 +184,52 @@ std::optional<std::string> Evaluate::parse_macro_command(std::string val) {
|
|||
// Check if command is >=, <= or !=
|
||||
std::string operation;
|
||||
char dc_register = 0;
|
||||
if(val.length() == 3) {
|
||||
operation = val.substr(0, 2);
|
||||
dc_register = val.at(2);
|
||||
if(token.length() == 3) {
|
||||
operation = token.substr(0, 2);
|
||||
dc_register = token.at(2);
|
||||
} else { // Otherwise it's either >, < or =
|
||||
operation = val.at(0);
|
||||
dc_register = val.at(1);
|
||||
operation = token.at(0);
|
||||
dc_register = token.at(1);
|
||||
}
|
||||
|
||||
// Macro commands works as follow
|
||||
// Macro commands works as follows
|
||||
// Pop two values off the stack and compares them assuming
|
||||
// they are numbers. If top-of-stack is greater,
|
||||
// execute register's content as a macro
|
||||
std::optional<std::string> err = std::nullopt;
|
||||
if(operation == ">") {
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::GT, dc_register, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::GT, dc_register);
|
||||
err = macro->exec(this->stack, this->parameters, this->regs);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(operation == "<") {
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::LT, dc_register, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::LT, dc_register);
|
||||
err = macro->exec(this->stack, this->parameters, this->regs);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(operation == "=") {
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::EQ, dc_register, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::EQ, dc_register);
|
||||
err = macro->exec(this->stack, this->parameters, this->regs);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(operation == ">=") {
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::GEQ, dc_register, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::GEQ, dc_register);
|
||||
err = macro->exec(this->stack, this->parameters, this->regs);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(operation == "<=") {
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::LEQ, dc_register, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::LEQ, dc_register);
|
||||
err = macro->exec(this->stack, this->parameters, this->regs);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
} else if(operation == "!=") {
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::NEQ, dc_register, this->regs, this->parameters);
|
||||
err = macro->exec(this->stack);
|
||||
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::NEQ, dc_register);
|
||||
err = macro->exec(this->stack, this->parameters, this->regs);
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
|
@ -412,11 +238,11 @@ std::optional<std::string> Evaluate::parse_macro_command(std::string val) {
|
|||
return err;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::parse_register_command(std::string val) {
|
||||
std::optional<std::string> Evaluate::parse_register_command(std::string token) {
|
||||
// A register command has length equal to 2
|
||||
// and starts either with 's', 'l'(i.e. "sX" or "lX")
|
||||
// or with 'S' or 'L'(i.e., "SX", "LX")
|
||||
if(val.at(0) == 's') {
|
||||
if(token.at(0) == 's') {
|
||||
// Check if main stack is empty
|
||||
if(this->stack.empty()) {
|
||||
return "This operation does not work on empty stack";
|
||||
|
@ -424,7 +250,7 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
|
|||
|
||||
// Otherwise pop an element from main stack and store it into
|
||||
// the register's top-of-the-stack. Any previous value gets overwritten
|
||||
auto reg_name = val.at(1);
|
||||
auto reg_name = token.at(1);
|
||||
auto head = this->stack.back();
|
||||
this->stack.pop_back();
|
||||
|
||||
|
@ -440,7 +266,7 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
|
|||
|
||||
// Populate register's 'reg_name' stack with top of main stack
|
||||
this->regs[reg_name].stack.push_back(head);
|
||||
} else if(val.at(0) == 'S') {
|
||||
} else if(token.at(0) == 'S') {
|
||||
// An uppercase 'S' pops the top of the main stack and
|
||||
// pushes it onto the stack of selected register.
|
||||
// The previous value of the register's stack becomes
|
||||
|
@ -451,7 +277,7 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
|
|||
return "This operation does not work on empty stack";
|
||||
}
|
||||
|
||||
auto reg_name = val.at(1);
|
||||
auto reg_name = token.at(1);
|
||||
auto head = this->stack.back();
|
||||
this->stack.pop_back();
|
||||
|
||||
|
@ -466,11 +292,11 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
|
|||
std::unordered_map<int, std::string>()
|
||||
};
|
||||
}
|
||||
} else if(val.at(0) == 'L') {
|
||||
} else if(token.at(0) == 'L') {
|
||||
// An uppercase 'L' pops the top of the register's stack
|
||||
// abd pushes it onto the main stack. The previous register's stack
|
||||
// value, if any, is accessible via the lowercase 'l' command
|
||||
auto reg_name = val.at(1);
|
||||
auto reg_name = token.at(1);
|
||||
|
||||
// Check if register exists
|
||||
if(this->regs.find(reg_name) == this->regs.end()) {
|
||||
|
@ -483,14 +309,14 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
|
|||
}
|
||||
|
||||
// Otherwise, pop an element from the register's stack and push it onto the main stack
|
||||
auto value = this->regs[reg_name].stack.back();
|
||||
auto ue = this->regs[reg_name].stack.back();
|
||||
this->regs[reg_name].stack.pop_back();
|
||||
this->stack.push_back(value);
|
||||
this->stack.push_back(ue);
|
||||
} else {
|
||||
// Otherwise retrieve the register name and push its value
|
||||
// to the stack without altering the register's stack.
|
||||
// If the register is empty, push '0' to the stack
|
||||
auto reg_name = val.at(1);
|
||||
auto reg_name = token.at(1);
|
||||
|
||||
// If register does not exist or its stack is empty, push '0' onto the main stack
|
||||
auto it = this->regs.find(reg_name);
|
||||
|
@ -500,21 +326,21 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
|
|||
}
|
||||
|
||||
// Otherwise, peek an element from the register's stack and push it onto the main stack
|
||||
auto value = this->regs[reg_name].stack.back();
|
||||
this->stack.push_back(value);
|
||||
auto ue = this->regs[reg_name].stack.back();
|
||||
this->stack.push_back(ue);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::parse_array_command(std::string val) {
|
||||
std::optional<std::string> Evaluate::parse_array_command(std::string token) {
|
||||
// An array command has length equal to 2, starts
|
||||
// with either ':'(store) or ';'(read) and ends with
|
||||
// the register name(i.e., ':X' or ';X')
|
||||
if(val.at(0) == ':') {
|
||||
// An ':' command pops two values from the main stack. The second-to-top
|
||||
if(token.at(0) == ':') {
|
||||
// An ':' command pops two ues from the main stack. The second-to-top
|
||||
// element will be stored in the array indexed by the top-of-stack.
|
||||
auto reg_name = val.at(1);
|
||||
auto reg_name = token.at(1);
|
||||
|
||||
// Check if the main stack has enough elements
|
||||
if(this->stack.size() < 2) {
|
||||
|
@ -539,7 +365,7 @@ std::optional<std::string> Evaluate::parse_array_command(std::string val) {
|
|||
// If array does not exist, allocate a new array first
|
||||
auto it = this->regs.find(reg_name);
|
||||
if(it != this->regs.end()) { // Register exists
|
||||
// Always discard previous values of array
|
||||
// Always discard previous ues of array
|
||||
it->second.array.erase(idx);
|
||||
it->second.array.insert(std::pair<int, std::string>(idx, arr_val));
|
||||
} else { // Register doesn't exist
|
||||
|
@ -551,7 +377,7 @@ std::optional<std::string> Evaluate::parse_array_command(std::string val) {
|
|||
} else {
|
||||
// An ';' command pops top-of-stack abd uses it as an index
|
||||
// for the array. The selected value, if any, is pushed onto the stack
|
||||
auto reg_name = val.at(1);
|
||||
auto reg_name = token.at(1);
|
||||
|
||||
// Check if the main stack is empty
|
||||
if(this->stack.empty()) {
|
||||
|
@ -595,86 +421,3 @@ std::optional<std::string> Evaluate::parse_array_command(std::string val) {
|
|||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::fn_set_precision() {
|
||||
// Check if stack has enough elements
|
||||
if(this->stack.empty()) {
|
||||
return "'k' requires one operand";
|
||||
}
|
||||
|
||||
// Check whether head is a non negative number
|
||||
auto head = this->stack.back();
|
||||
if(!is_num<int>(head) || std::stoi(head) < 0) {
|
||||
return "Precision must be a non-negative number";
|
||||
}
|
||||
|
||||
// Otherwise extract head of the stack and use it
|
||||
// to set precision parameter
|
||||
this->stack.pop_back();
|
||||
this->parameters.precision = std::stoi(head);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::fn_get_precision() {
|
||||
this->stack.push_back(std::to_string(this->parameters.precision));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::fn_set_oradix() {
|
||||
// Check if stack has enough elements
|
||||
if(this->stack.empty()) {
|
||||
return "'o' requires one operand";
|
||||
}
|
||||
|
||||
// Check whether the head is a number
|
||||
auto head = this->stack.back();
|
||||
if(!is_num<int>(head)) {
|
||||
return "'o' requires numeric values only";
|
||||
}
|
||||
|
||||
// Otherwise convert it to int
|
||||
auto oradix = std::stoi(head);
|
||||
switch(oradix) {
|
||||
case 2: this->parameters.oradix = radix_base::BIN; break;
|
||||
case 8: this->parameters.oradix = radix_base::OCT; break;
|
||||
case 10: this->parameters.oradix = radix_base::DEC; break;
|
||||
case 16: this->parameters.oradix = radix_base::HEX; break;
|
||||
default: return "'o' accepts either BIN, OCT, DEC or HEX bases";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::fn_get_oradix() {
|
||||
this->stack.push_back(std::to_string(static_cast<int>(this->parameters.oradix)));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::fn_set_iradix() {
|
||||
// Check if stack has enough elements
|
||||
if(this->stack.empty()) {
|
||||
return "'i' requires one operand";
|
||||
}
|
||||
|
||||
// Check whether head is a number within the range 2-16
|
||||
auto head = this->stack.back();
|
||||
if(!is_num<double>(head) || std::stoi(head) < 2 || std::stoi(head) > 16) {
|
||||
return "Input base must be a number within the range 2-16(inclusive)";
|
||||
}
|
||||
|
||||
// Otherwise extract head of the stack and use it
|
||||
// to set input base
|
||||
this->stack.pop_back();
|
||||
this->parameters.iradix = std::stoi(head);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::fn_get_iradix() {
|
||||
this->stack.push_back(std::to_string(this->parameters.iradix));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
20
src/eval.h
20
src/eval.h
|
@ -3,7 +3,10 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include "operation.h"
|
||||
#include "types.h"
|
||||
|
||||
class Evaluate {
|
||||
|
@ -18,19 +21,16 @@ public:
|
|||
private:
|
||||
std::optional<std::string> handle_special(std::string val, size_t &idx);
|
||||
std::optional<std::string> parse_macro(size_t &idx);
|
||||
std::optional<std::string> parse_macro_command(std::string val);
|
||||
std::optional<std::string> parse_register_command(std::string val);
|
||||
std::optional<std::string> parse_array_command(std::string val);
|
||||
std::optional<std::string> parse_base_n(const std::string& val);
|
||||
std::optional<std::string> fn_set_precision();
|
||||
std::optional<std::string> fn_get_precision();
|
||||
std::optional<std::string> fn_set_oradix();
|
||||
std::optional<std::string> fn_get_oradix();
|
||||
std::optional<std::string> fn_set_iradix();
|
||||
std::optional<std::string> fn_get_iradix();
|
||||
std::optional<std::string> parse_macro_command(std::string token);
|
||||
std::optional<std::string> parse_register_command(std::string token);
|
||||
std::optional<std::string> parse_array_command(std::string token);
|
||||
std::optional<std::string> parse_base_n(const std::string& token);
|
||||
void init_environment();
|
||||
|
||||
using op_factory_t = std::function<std::unique_ptr<IOperation>()>;
|
||||
std::vector<std::string> expr;
|
||||
std::unordered_map<char, Register> ®s;
|
||||
std::unordered_map<std::string, op_factory_t> op_factory;
|
||||
dc_stack_t &stack;
|
||||
Parameters ¶meters;
|
||||
};
|
||||
|
|
|
@ -7,20 +7,20 @@
|
|||
#include "macro.h"
|
||||
#include "is_num.h"
|
||||
|
||||
std::optional<std::string> Macro::exec(dc_stack_t &stack) {
|
||||
std::optional<std::string> Macro::exec(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) {
|
||||
std::optional<std::string> err = std::nullopt;
|
||||
|
||||
switch(this->op_type) {
|
||||
case OPType::EX: err = fn_execute(stack); break;
|
||||
case OPType::CMP: err = fn_evaluate_macro(stack); break;
|
||||
case OPType::RI: err = fn_read_input(stack); break;
|
||||
case OPType::EX: err = fn_execute(stack, parameters, regs); break;
|
||||
case OPType::CMP: err = fn_evaluate_macro(stack, parameters, regs); break;
|
||||
case OPType::RI: err = fn_read_input(stack, parameters, regs); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
std::optional<std::string> Macro::fn_execute(dc_stack_t &stack) {
|
||||
std::optional<std::string> Macro::fn_execute(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "This operation does not work on empty stack";
|
||||
|
@ -32,7 +32,7 @@ std::optional<std::string> Macro::fn_execute(dc_stack_t &stack) {
|
|||
if(!is_num<double>(head)) {
|
||||
stack.pop_back();
|
||||
std::vector<std::string> tokens = split(head);
|
||||
Evaluate evaluator(tokens, this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
@ -43,14 +43,14 @@ std::optional<std::string> Macro::fn_execute(dc_stack_t &stack) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
||||
std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) {
|
||||
// Check whether the main stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "This operation requires two elements";
|
||||
}
|
||||
|
||||
// Check whether the register's stack exists or not
|
||||
if(this->regs.find(this->dc_register) == this->regs.end()) {
|
||||
if(regs.find(this->dc_register) == regs.end()) {
|
||||
return "Null register";
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
stack.pop_back();
|
||||
auto second_str = stack.back();
|
||||
stack.pop_back();
|
||||
auto dc_macro = this->regs[this->dc_register].stack.back();
|
||||
auto dc_macro = regs[this->dc_register].stack.back();
|
||||
|
||||
// Check if macro exists and if top two elements of main stack are numbers
|
||||
if(!dc_macro.empty() && is_num<double>(head_str) && is_num<double>(second_str)) {
|
||||
|
@ -70,7 +70,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
case Operator::GT: {
|
||||
if(head > second) {
|
||||
std::vector<std::string> tokens = split(dc_macro);
|
||||
Evaluate evaluator(tokens, this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
@ -82,7 +82,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
case Operator::LT: {
|
||||
if(head < second) {
|
||||
std::vector<std::string> tokens = split(dc_macro);
|
||||
Evaluate evaluator(tokens, this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
@ -94,7 +94,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
case Operator::EQ: {
|
||||
if(head == second) {
|
||||
std::vector<std::string> tokens = split(dc_macro);
|
||||
Evaluate evaluator(tokens, this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
@ -106,7 +106,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
case Operator::GEQ: {
|
||||
if(head >= second) {
|
||||
std::vector<std::string> tokens = split(dc_macro);
|
||||
Evaluate evaluator(tokens, this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
@ -118,7 +118,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
case Operator::LEQ: {
|
||||
if(head <= second) {
|
||||
std::vector<std::string> tokens = split(dc_macro);
|
||||
Evaluate evaluator(tokens, this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
@ -130,7 +130,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
case Operator::NEQ: {
|
||||
if(head != second) {
|
||||
std::vector<std::string> tokens = split(dc_macro);
|
||||
Evaluate evaluator(tokens, this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
@ -145,7 +145,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(dc_stack_t &stack) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Macro::fn_read_input(dc_stack_t &stack) {
|
||||
std::optional<std::string> Macro::fn_read_input(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) {
|
||||
// Read user input from stdin
|
||||
std::string user_input;
|
||||
|
||||
|
@ -156,7 +156,7 @@ std::optional<std::string> Macro::fn_read_input(dc_stack_t &stack) {
|
|||
|
||||
// Push the input onto the main stack and execute it as a macro
|
||||
stack.push_back(user_input);
|
||||
Evaluate evaluator(this->regs, stack, this->parameters);
|
||||
Evaluate evaluator(regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
|
16
src/macro.h
16
src/macro.h
|
@ -8,21 +8,17 @@ enum class Operator {
|
|||
|
||||
class Macro : public IOperation {
|
||||
public:
|
||||
Macro(const OPType op_t, const Operator o, const char dc_r, std::unordered_map<char, Register> &r, Parameters &p)
|
||||
: op_type(op_t), op(o), dc_register(dc_r), regs(r), parameters(p) {}
|
||||
Macro(const OPType op_t, std::unordered_map<char, Register> &r, Parameters &p)
|
||||
: op_type(op_t), regs(r), parameters(p) {}
|
||||
std::optional<std::string> exec(dc_stack_t &stack) override;
|
||||
Macro(const OPType op_t, const Operator o, const char dc_r) : op_type(op_t), op(o), dc_register(dc_r) {}
|
||||
explicit Macro(const OPType op_t) : op_type(op_t) {}
|
||||
std::optional<std::string> exec(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) override;
|
||||
static std::vector<std::string> split(const std::string& str);
|
||||
|
||||
private:
|
||||
std::optional<std::string> fn_execute(dc_stack_t &stack);
|
||||
std::optional<std::string> fn_evaluate_macro(dc_stack_t &stack);
|
||||
std::optional<std::string> fn_read_input(dc_stack_t &stack);
|
||||
static std::optional<std::string> fn_execute(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s);
|
||||
std::optional<std::string> fn_evaluate_macro(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s);
|
||||
static std::optional<std::string> fn_read_input(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s);
|
||||
|
||||
OPType op_type;
|
||||
Operator op{};
|
||||
char dc_register{};
|
||||
std::unordered_map<char, Register> ®s;
|
||||
Parameters ¶meters;
|
||||
};
|
||||
|
|
112
src/math.cpp
112
src/math.cpp
|
@ -5,35 +5,35 @@
|
|||
#include "math.h"
|
||||
#include "is_num.h"
|
||||
|
||||
std::optional<std::string> Math::exec(dc_stack_t &stack) {
|
||||
std::optional<std::string> Math::exec(dc_stack_t &stack, Parameters ¶meters, __attribute__((unused)) std::unordered_map<char, Register> ®s) {
|
||||
std::optional<std::string> err = std::nullopt;
|
||||
|
||||
switch(this->op_type) {
|
||||
case OPType::ADD: err = fn_add(stack); break;
|
||||
case OPType::SUB: err = fn_sub(stack); break;
|
||||
case OPType::MUL: err = fn_mul(stack); break;
|
||||
case OPType::DIV: err = fn_div(stack); break;
|
||||
case OPType::MOD: err = fn_mod(stack); break;
|
||||
case OPType::DIV_MOD: err = fn_div_mod(stack); break;
|
||||
case OPType::MOD_EXP: err = fn_mod_exp(stack); break;
|
||||
case OPType::EXP: err = fn_exp(stack); break;
|
||||
case OPType::SQRT: err = fn_sqrt(stack); break;
|
||||
case OPType::SIN: err = fn_sin(stack); break;
|
||||
case OPType::COS: err = fn_cos(stack); break;
|
||||
case OPType::TAN: err = fn_tan(stack); break;
|
||||
case OPType::ASIN: err = fn_asin(stack); break;
|
||||
case OPType::ACOS: err = fn_acos(stack); break;
|
||||
case OPType::ATAN: err = fn_atan(stack); break;
|
||||
case OPType::FACT: err = fn_fact(stack); break;
|
||||
case OPType::PI: err = fn_pi(stack); break;
|
||||
case OPType::E: err = fn_e(stack); break;
|
||||
case OPType::ADD: err = fn_add(stack, parameters); break;
|
||||
case OPType::SUB: err = fn_sub(stack, parameters); break;
|
||||
case OPType::MUL: err = fn_mul(stack, parameters); break;
|
||||
case OPType::DIV: err = fn_div(stack, parameters); break;
|
||||
case OPType::MOD: err = fn_mod(stack, parameters); break;
|
||||
case OPType::DIV_MOD: err = fn_div_mod(stack, parameters); break;
|
||||
case OPType::MOD_EXP: err = fn_mod_exp(stack, parameters); break;
|
||||
case OPType::EXP: err = fn_exp(stack, parameters); break;
|
||||
case OPType::SQRT: err = fn_sqrt(stack, parameters); break;
|
||||
case OPType::SIN: err = fn_sin(stack, parameters); break;
|
||||
case OPType::COS: err = fn_cos(stack, parameters); break;
|
||||
case OPType::TAN: err = fn_tan(stack, parameters); break;
|
||||
case OPType::ASIN: err = fn_asin(stack, parameters); break;
|
||||
case OPType::ACOS: err = fn_acos(stack, parameters); break;
|
||||
case OPType::ATAN: err = fn_atan(stack, parameters); break;
|
||||
case OPType::FACT: err = fn_fact(stack, parameters); break;
|
||||
case OPType::PI: err = fn_pi(stack, parameters); break;
|
||||
case OPType::E: err = fn_e(stack, parameters); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_add(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_add(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'+' requires two operands";
|
||||
|
@ -55,7 +55,7 @@ std::optional<std::string> Math::fn_add(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits((lhs + rhs), this->precision));
|
||||
stack.push_back(trim_digits((lhs + rhs), parameters.precision));
|
||||
} else {
|
||||
return "'+' requires numeric values";
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ std::optional<std::string> Math::fn_add(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_sub(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_sub(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'-' requires two operands";
|
||||
|
@ -95,7 +95,7 @@ std::optional<std::string> Math::fn_sub(dc_stack_t &stack) const {
|
|||
}
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(result, this->precision));
|
||||
stack.push_back(trim_digits(result, parameters.precision));
|
||||
} else {
|
||||
return "'-' requires numeric values";
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ std::optional<std::string> Math::fn_sub(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_mul(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_mul(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'*' requires two operands";
|
||||
|
@ -125,7 +125,7 @@ std::optional<std::string> Math::fn_mul(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits((lhs * rhs), this->precision));
|
||||
stack.push_back(trim_digits((lhs * rhs), parameters.precision));
|
||||
} else {
|
||||
return "'*' requires numeric values";
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ std::optional<std::string> Math::fn_mul(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_div(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_div(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'/' requires two operands";
|
||||
|
@ -160,7 +160,7 @@ std::optional<std::string> Math::fn_div(dc_stack_t &stack) const {
|
|||
}
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits((dividend / divisor), this->precision));
|
||||
stack.push_back(trim_digits((dividend / divisor), parameters.precision));
|
||||
} else {
|
||||
return "'/' requires numeric values";
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ std::optional<std::string> Math::fn_div(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_mod(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_mod(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'%' requires two operands";
|
||||
|
@ -195,7 +195,7 @@ std::optional<std::string> Math::fn_mod(dc_stack_t &stack) const {
|
|||
}
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits((static_cast<int>(lhs) % static_cast<int>(rhs)), this->precision));
|
||||
stack.push_back(trim_digits((static_cast<int>(lhs) % static_cast<int>(rhs)), parameters.precision));
|
||||
} else {
|
||||
return "'%' requires numeric values";
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ std::optional<std::string> Math::fn_mod(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_div_mod(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_div_mod(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'~' requires two operands";
|
||||
|
@ -229,8 +229,8 @@ std::optional<std::string> Math::fn_div_mod(dc_stack_t &stack) const {
|
|||
auto quotient = std::trunc(dividend / divisor);
|
||||
auto remainder = ((int)dividend % (int)divisor);
|
||||
|
||||
stack.push_back(trim_digits(quotient, this->precision));
|
||||
stack.push_back(trim_digits(remainder, this->precision));
|
||||
stack.push_back(trim_digits(quotient, parameters.precision));
|
||||
stack.push_back(trim_digits(remainder, parameters.precision));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -240,7 +240,7 @@ std::optional<std::string> Math::fn_div_mod(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_mod_exp(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_mod_exp(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 3) {
|
||||
return "'|' requires three operands";
|
||||
|
@ -285,7 +285,7 @@ std::optional<std::string> Math::fn_mod_exp(dc_stack_t &stack) const {
|
|||
c = (c * base) % modulus;
|
||||
}
|
||||
|
||||
stack.push_back(trim_digits(c, this->precision));
|
||||
stack.push_back(trim_digits(c, parameters.precision));
|
||||
} else {
|
||||
return "'|' requires numeric values";
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ std::optional<std::string> Math::fn_mod_exp(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_exp(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_exp(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'^' requires two operands";
|
||||
|
@ -315,7 +315,7 @@ std::optional<std::string> Math::fn_exp(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(pow(base, exp), this->precision));
|
||||
stack.push_back(trim_digits(pow(base, exp), parameters.precision));
|
||||
} else {
|
||||
return "'^' requires numeric values";
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ std::optional<std::string> Math::fn_exp(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_sqrt(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_sqrt(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'v' requires one operand";
|
||||
|
@ -344,7 +344,7 @@ std::optional<std::string> Math::fn_sqrt(dc_stack_t &stack) const {
|
|||
}
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(sqrt(val), this->precision));
|
||||
stack.push_back(trim_digits(sqrt(val), parameters.precision));
|
||||
} else {
|
||||
return "'v' requires numeric values";
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ std::optional<std::string> Math::fn_sqrt(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_sin(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_sin(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'sin' requires one operand";
|
||||
|
@ -369,7 +369,7 @@ std::optional<std::string> Math::fn_sin(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(sin(val), this->precision));
|
||||
stack.push_back(trim_digits(sin(val), parameters.precision));
|
||||
} else {
|
||||
return "'sin' requires numeric values";
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ std::optional<std::string> Math::fn_sin(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_cos(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_cos(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'cos' requires one operand";
|
||||
|
@ -394,7 +394,7 @@ std::optional<std::string> Math::fn_cos(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(cos(val), this->precision));
|
||||
stack.push_back(trim_digits(cos(val), parameters.precision));
|
||||
} else {
|
||||
return "'cos' requires numeric values";
|
||||
}
|
||||
|
@ -402,7 +402,7 @@ std::optional<std::string> Math::fn_cos(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_tan(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_tan(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'tan' requires one operand";
|
||||
|
@ -419,7 +419,7 @@ std::optional<std::string> Math::fn_tan(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(tan(val), this->precision));
|
||||
stack.push_back(trim_digits(tan(val), parameters.precision));
|
||||
} else {
|
||||
return "'tan' requires numeric values";
|
||||
}
|
||||
|
@ -427,7 +427,7 @@ std::optional<std::string> Math::fn_tan(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_asin(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_asin(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'asin' requires one operand";
|
||||
|
@ -444,7 +444,7 @@ std::optional<std::string> Math::fn_asin(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(asin(val), this->precision));
|
||||
stack.push_back(trim_digits(asin(val), parameters.precision));
|
||||
} else {
|
||||
return "'asin' requires numeric values";
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ std::optional<std::string> Math::fn_asin(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_acos(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_acos(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'acos' requires one operand";
|
||||
|
@ -469,7 +469,7 @@ std::optional<std::string> Math::fn_acos(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(acos(val), this->precision));
|
||||
stack.push_back(trim_digits(acos(val), parameters.precision));
|
||||
} else {
|
||||
return "'acos' requires numeric values";
|
||||
}
|
||||
|
@ -477,7 +477,7 @@ std::optional<std::string> Math::fn_acos(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_atan(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_atan(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'atan' requires one operand";
|
||||
|
@ -494,7 +494,7 @@ std::optional<std::string> Math::fn_atan(dc_stack_t &stack) const {
|
|||
stack.pop_back();
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(atan(val), this->precision));
|
||||
stack.push_back(trim_digits(atan(val), parameters.precision));
|
||||
} else {
|
||||
return "'atan' requires numeric values";
|
||||
}
|
||||
|
@ -502,7 +502,7 @@ std::optional<std::string> Math::fn_atan(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_fact(dc_stack_t &stack) const {
|
||||
std::optional<std::string> Math::fn_fact(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'!' requires one operand";
|
||||
|
@ -528,7 +528,7 @@ std::optional<std::string> Math::fn_fact(dc_stack_t &stack) const {
|
|||
}
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push_back(trim_digits(static_cast<double>(factorial), this->precision));
|
||||
stack.push_back(trim_digits(static_cast<double>(factorial), parameters.precision));
|
||||
} else {
|
||||
return "'!' requires numeric values";
|
||||
}
|
||||
|
@ -536,14 +536,14 @@ std::optional<std::string> Math::fn_fact(dc_stack_t &stack) const {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_pi(dc_stack_t &stack) const {
|
||||
stack.push_back(trim_digits(std::numbers::pi, this->precision));
|
||||
std::optional<std::string> Math::fn_pi(dc_stack_t &stack, Parameters ¶meters) {
|
||||
stack.push_back(trim_digits(std::numbers::pi, parameters.precision));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Math::fn_e(dc_stack_t &stack) const {
|
||||
stack.push_back(trim_digits(std::numbers::e, this->precision));
|
||||
std::optional<std::string> Math::fn_e(dc_stack_t &stack, Parameters ¶meters) {
|
||||
stack.push_back(trim_digits(std::numbers::e, parameters.precision));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
41
src/math.h
41
src/math.h
|
@ -4,30 +4,29 @@
|
|||
|
||||
class Math : public IOperation {
|
||||
public:
|
||||
Math(const OPType op_t, const unsigned int p) : op_type(op_t), precision(p) {}
|
||||
std::optional<std::string> exec(dc_stack_t &stack) override;
|
||||
explicit Math(const OPType op_t) : op_type(op_t) {}
|
||||
std::optional<std::string> exec(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) override;
|
||||
|
||||
private:
|
||||
std::optional<std::string> fn_add(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_sub(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_mul(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_div(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_mod(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_div_mod(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_mod_exp(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_exp(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_sqrt(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_sin(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_cos(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_tan(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_asin(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_acos(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_atan(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_fact(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_pi(dc_stack_t &stack) const;
|
||||
std::optional<std::string> fn_e(dc_stack_t &stack) const;
|
||||
static std::optional<std::string> fn_add(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_sub(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_mul(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_div(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_mod(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_div_mod(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_mod_exp(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_exp(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_sqrt(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_sin(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_cos(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_tan(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_asin(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_acos(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_atan(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_fact(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_pi(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::optional<std::string> fn_e(dc_stack_t &stack, Parameters ¶meters) ;
|
||||
static std::string trim_digits(double number, unsigned int precision);
|
||||
|
||||
OPType op_type;
|
||||
unsigned int precision;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
class IOperation {
|
||||
public:
|
||||
virtual std::optional<std::string> exec(dc_stack_t &stack) = 0;
|
||||
virtual std::optional<std::string> exec(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) = 0;
|
||||
virtual ~IOperation() = default;
|
||||
};
|
||||
|
||||
|
@ -14,7 +14,8 @@ enum class OPType {
|
|||
ADD, SUB, MUL, DIV, MOD, DIV_MOD, MOD_EXP, EXP,
|
||||
SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN, FACT, PI, E,
|
||||
// Stack operations
|
||||
PCG, P, PBB, PBH, PBO, CLR, PH, SO, DP, PS, CH, CS,
|
||||
PCG, P, PBB, PBH, PBO, CLR, PH, SO, DP, PS, CH, CS,
|
||||
SP, GP, SOR, GOR, SIR, GIR,
|
||||
// Macro operations
|
||||
EX, CMP, RI
|
||||
};
|
||||
|
|
127
src/stack.cpp
127
src/stack.cpp
|
@ -5,49 +5,55 @@
|
|||
#include "stack.h"
|
||||
#include "is_num.h"
|
||||
|
||||
std::optional<std::string> Stack::exec(dc_stack_t &stack) {
|
||||
std::optional<std::string> Stack::exec(dc_stack_t &stack, Parameters ¶meters, __attribute__((unused)) std::unordered_map<char, Register> ®s) {
|
||||
std::optional<std::string> err = std::nullopt;
|
||||
|
||||
auto print_oradix = [this, &stack](radix_base base) {
|
||||
auto old_rdx = this->oradix;
|
||||
this->oradix = base;
|
||||
auto res = fn_print(stack, false);
|
||||
this->oradix = old_rdx;
|
||||
auto print_oradix = [&stack, ¶meters, this](radix_base base) {
|
||||
auto old_rdx = parameters.oradix;
|
||||
parameters.oradix = base;
|
||||
auto res = fn_print(stack, parameters, false);
|
||||
parameters.oradix = old_rdx;
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
switch(this->op_type) {
|
||||
case OPType::PCG: err = fn_print(stack, true); break;
|
||||
case OPType::P: err = fn_print(stack, false); break;
|
||||
case OPType::PCG: err = fn_print(stack, parameters, true); break;
|
||||
case OPType::P: err = fn_print(stack, parameters, false); break;
|
||||
case OPType::PBB: err = print_oradix(radix_base::BIN); break;
|
||||
case OPType::PBO: err = print_oradix(radix_base::OCT); break;
|
||||
case OPType::PBH: err = print_oradix(radix_base::HEX); break;
|
||||
case OPType::CLR: stack.clear(); break;
|
||||
case OPType::PH: fn_pop_head(stack); break;
|
||||
case OPType::SO: fn_swap_xy(stack); break;
|
||||
case OPType::DP: fn_dup_head(stack); break;
|
||||
case OPType::PS: fn_print_stack(stack); break;
|
||||
case OPType::CH: fn_head_size(stack); break;
|
||||
case OPType::CS: fn_stack_size(stack); break;
|
||||
case OPType::PH: err = fn_pop_head(stack); break;
|
||||
case OPType::SO: err = fn_swap_xy(stack); break;
|
||||
case OPType::DP: err = fn_dup_head(stack); break;
|
||||
case OPType::PS: err = fn_print_stack(stack, parameters); break;
|
||||
case OPType::CH: err = fn_head_size(stack); break;
|
||||
case OPType::CS: err = fn_stack_size(stack); break;
|
||||
case OPType::SP: err = fn_set_precision(stack, parameters); break;
|
||||
case OPType::GP: err = fn_get_precision(stack, parameters); break;
|
||||
case OPType::SOR: err = fn_set_oradix(stack, parameters); break;
|
||||
case OPType::GOR: err = fn_get_oradix(stack, parameters); break;
|
||||
case OPType::SIR: err = fn_set_iradix(stack, parameters); break;
|
||||
case OPType::GIR: err = fn_get_iradix(stack, parameters); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_print(dc_stack_t &stack, bool new_line) {
|
||||
std::optional<std::string> Stack::fn_print(dc_stack_t &stack, Parameters ¶meters, bool new_line) {
|
||||
// Check if the stack is empty
|
||||
if(stack.empty()) {
|
||||
return "Cannot print empty stack";
|
||||
}
|
||||
|
||||
// If the output radix is non-decimal, check if top of the stack is an integer
|
||||
if(static_cast<int>(this->oradix) != 10 && !is_num<int>(stack.back())) {
|
||||
if(static_cast<int>(parameters.oradix) != 10 && !is_num<int>(stack.back())) {
|
||||
return "This output radix requires integer values";
|
||||
}
|
||||
|
||||
switch(this->oradix) {
|
||||
switch(parameters.oradix) {
|
||||
case radix_base::DEC: {
|
||||
if(new_line) {
|
||||
std::cout << stack.back() << std::endl;
|
||||
|
@ -117,8 +123,8 @@ std::optional<std::string> Stack::fn_dup_head(dc_stack_t &stack) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_print_stack(dc_stack_t &stack) {
|
||||
switch(this->oradix) {
|
||||
std::optional<std::string> Stack::fn_print_stack(dc_stack_t &stack, Parameters ¶meters) {
|
||||
switch(parameters.oradix) {
|
||||
case radix_base::DEC: {
|
||||
for(auto & it : std::ranges::reverse_view(stack)) {
|
||||
std::cout << it << std::endl;
|
||||
|
@ -186,6 +192,89 @@ std::optional<std::string> Stack::fn_stack_size(dc_stack_t &stack) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_set_precision(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'k' requires one operand";
|
||||
}
|
||||
|
||||
// Check whether head is a non-negative number
|
||||
auto head = stack.back();
|
||||
if(!is_num<int>(head) || std::stoi(head) < 0) {
|
||||
return "Precision must be a non-negative number";
|
||||
}
|
||||
|
||||
// Otherwise extract head of the stack and use it
|
||||
// to set precision parameter
|
||||
stack.pop_back();
|
||||
parameters.precision = std::stoi(head);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_get_precision(dc_stack_t &stack, Parameters ¶meters) {
|
||||
stack.push_back(std::to_string(parameters.precision));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_set_oradix(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'o' requires one operand";
|
||||
}
|
||||
|
||||
// Check whether the head is a number
|
||||
auto head = stack.back();
|
||||
if(!is_num<int>(head)) {
|
||||
return "'o' requires numeric values only";
|
||||
}
|
||||
|
||||
// Otherwise convert it to int
|
||||
auto oradix = std::stoi(head);
|
||||
switch(oradix) {
|
||||
case 2: parameters.oradix = radix_base::BIN; break;
|
||||
case 8: parameters.oradix = radix_base::OCT; break;
|
||||
case 10: parameters.oradix = radix_base::DEC; break;
|
||||
case 16: parameters.oradix = radix_base::HEX; break;
|
||||
default: return "'o' accepts either BIN, OCT, DEC or HEX bases";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_get_oradix(dc_stack_t &stack, Parameters ¶meters) {
|
||||
stack.push_back(std::to_string(static_cast<int>(parameters.oradix)));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_set_iradix(dc_stack_t &stack, Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'i' requires one operand";
|
||||
}
|
||||
|
||||
// Check whether head is a number within the range 2-16
|
||||
auto head = stack.back();
|
||||
if(!is_num<double>(head) || std::stoi(head) < 2 || std::stoi(head) > 16) {
|
||||
return "Input base must be a number within the range 2-16(inclusive)";
|
||||
}
|
||||
|
||||
// Otherwise extract head of the stack and use it
|
||||
// to set input base
|
||||
stack.pop_back();
|
||||
parameters.iradix = std::stoi(head);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Stack::fn_get_iradix(dc_stack_t &stack, Parameters ¶meters) {
|
||||
stack.push_back(std::to_string(parameters.iradix));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr std::string Stack::to_bin(auto num) {
|
||||
if(num == 0) {
|
||||
return "0";
|
||||
|
|
14
src/stack.h
14
src/stack.h
|
@ -5,19 +5,23 @@
|
|||
class Stack : public IOperation {
|
||||
public:
|
||||
explicit Stack(const OPType op_t) : op_type(op_t) {}
|
||||
Stack(const OPType op_t, const radix_base base) : op_type(op_t), oradix(base) {}
|
||||
std::optional<std::string> exec(dc_stack_t &stack) override;
|
||||
std::optional<std::string> exec(dc_stack_t &stack, Parameters ¶meters, std::unordered_map<char, Register> ®s) override;
|
||||
|
||||
private:
|
||||
std::optional<std::string> fn_print(dc_stack_t &stack, bool new_line);
|
||||
std::optional<std::string> fn_print(dc_stack_t &stack, Parameters ¶meters, bool new_line);
|
||||
static std::optional<std::string> fn_pop_head(dc_stack_t &stack);
|
||||
static std::optional<std::string> fn_swap_xy(dc_stack_t &stack);
|
||||
static std::optional<std::string> fn_dup_head(dc_stack_t &stack);
|
||||
std::optional<std::string> fn_print_stack(dc_stack_t &stack);
|
||||
std::optional<std::string> fn_print_stack(dc_stack_t &stack, Parameters ¶meters);
|
||||
static std::optional<std::string> fn_head_size(dc_stack_t &stack);
|
||||
static std::optional<std::string> fn_stack_size(dc_stack_t &stack);
|
||||
static std::optional<std::string> fn_set_precision(dc_stack_t &stack, Parameters ¶meters);
|
||||
static std::optional<std::string> fn_get_precision(dc_stack_t &stack, Parameters ¶meters);
|
||||
static std::optional<std::string> fn_set_oradix(dc_stack_t &stack, Parameters ¶meters);
|
||||
static std::optional<std::string> fn_get_oradix(dc_stack_t &stack, Parameters ¶meters);
|
||||
static std::optional<std::string> fn_set_iradix(dc_stack_t &stack, Parameters ¶meters);
|
||||
static std::optional<std::string> fn_get_iradix(dc_stack_t &stack, Parameters ¶meters);
|
||||
constexpr std::string to_bin(auto num);
|
||||
|
||||
OPType op_type;
|
||||
radix_base oradix{};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue