Compare commits

...

4 Commits

Author SHA1 Message Date
Marco Cetica f349442410
Completed parser refactoring
dc / build (push) Has been cancelled Details
2024-03-08 16:26:26 +01:00
Marco Cetica d0ffb8610a
Fixed bug about macros
dc / build (push) Successful in 46s Details
2024-03-08 15:31:45 +01:00
Marco Cetica b9235470e3
Moved radix and precision commands to Stack class
dc / build (push) Failing after 46s Details
2024-03-08 12:18:26 +01:00
Marco Cetica 505ed8b148
Started refactoring of the parser/evaluator method 2024-03-08 11:42:57 +01:00
10 changed files with 338 additions and 506 deletions

View File

@ -1,7 +1,7 @@
name: dc
on:
push:
branches: [master, unit_testing]
branches: [master, factory_parsing]
workflow_dispatch:
jobs:

View File

@ -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;
}

View File

@ -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> &regs;
std::unordered_map<std::string, op_factory_t> op_factory;
dc_stack_t &stack;
Parameters &parameters;
};

View File

@ -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 &parameters, std::unordered_map<char, Register> &regs) {
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 &parameters, std::unordered_map<char, Register> &regs) {
// 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 &parameters, std::unordered_map<char, Register> &regs) {
// 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 &parameters, std::unordered_map<char, Register> &regs) {
// 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) {

View File

@ -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 &parameters, std::unordered_map<char, Register> &regs) 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 &parameters, std::unordered_map<char, Register> &regs);
std::optional<std::string> fn_evaluate_macro(dc_stack_t &stack, Parameters &parameters, std::unordered_map<char, Register> &regs);
static std::optional<std::string> fn_read_input(dc_stack_t &stack, Parameters &parameters, std::unordered_map<char, Register> &regs);
OPType op_type;
Operator op{};
char dc_register{};
std::unordered_map<char, Register> &regs;
Parameters &parameters;
};

View File

@ -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 &parameters, __attribute__((unused)) std::unordered_map<char, Register> &regs) {
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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
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 &parameters) {
stack.push_back(trim_digits(std::numbers::e, parameters.precision));
return std::nullopt;
}

View File

@ -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 &parameters, std::unordered_map<char, Register> &regs) 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 &parameters) ;
static std::optional<std::string> fn_sub(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_mul(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_div(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_mod(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_div_mod(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_mod_exp(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_exp(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_sqrt(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_sin(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_cos(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_tan(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_asin(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_acos(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_atan(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_fact(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_pi(dc_stack_t &stack, Parameters &parameters) ;
static std::optional<std::string> fn_e(dc_stack_t &stack, Parameters &parameters) ;
static std::string trim_digits(double number, unsigned int precision);
OPType op_type;
unsigned int precision;
};

View File

@ -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 &parameters, std::unordered_map<char, Register> &regs) = 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
};

View File

@ -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 &parameters, __attribute__((unused)) std::unordered_map<char, Register> &regs) {
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, &parameters, 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 &parameters, 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 &parameters) {
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 &parameters) {
// 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 &parameters) {
stack.push_back(std::to_string(parameters.precision));
return std::nullopt;
}
std::optional<std::string> Stack::fn_set_oradix(dc_stack_t &stack, Parameters &parameters) {
// 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 &parameters) {
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 &parameters) {
// 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 &parameters) {
stack.push_back(std::to_string(parameters.iradix));
return std::nullopt;
}
constexpr std::string Stack::to_bin(auto num) {
if(num == 0) {
return "0";

View File

@ -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 &parameters, std::unordered_map<char, Register> &regs) 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 &parameters, 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 &parameters);
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 &parameters);
static std::optional<std::string> fn_get_precision(dc_stack_t &stack, Parameters &parameters);
static std::optional<std::string> fn_set_oradix(dc_stack_t &stack, Parameters &parameters);
static std::optional<std::string> fn_get_oradix(dc_stack_t &stack, Parameters &parameters);
static std::optional<std::string> fn_set_iradix(dc_stack_t &stack, Parameters &parameters);
static std::optional<std::string> fn_get_iradix(dc_stack_t &stack, Parameters &parameters);
constexpr std::string to_bin(auto num);
OPType op_type;
radix_base oradix{};
};