Added 'print_base' function

This commit is contained in:
Marco Cetica 2023-11-07 10:36:32 +01:00
parent 137c781ee3
commit f75ac99625
Signed by: marco
GPG Key ID: 45060A949E90D0FD
6 changed files with 121 additions and 24 deletions

View File

@ -24,6 +24,7 @@ Some of the supported features are:
- Basic arithmetical operations(`+`, `-`, `*`, `/`, `^`, `%`);
- Scientific notation support(`5e3` -> `5000`);
- Trigonometrical functions(`sin`, `cos`, `tan`);
- Base conversion(binary: `pb`, octal: `po`, hexadecimal: `px`);
- Factorial and constants(`!`, `pi`, `e`);
- Stack operations:
- Print top element(`p`, `P`);

16
man.md
View File

@ -3,7 +3,7 @@ title: dc
section: 1
header: General Commands Manual
footer: Marco Cetica
date: November 06, 2023
date: November 07, 2023
---
@ -186,6 +186,20 @@ Pops one value, computes its `cos`, and pushes that.
Pops one value, computes its `tan`, and pushes that.
## Base Conversion
**pb**
Prints the value on the top of the stack in base 2, without altering the stack. A newline is printed after the value.
**po**
Prints the value on the top of the stack in base 8, without altering the stack. A newline is printed after the value.
**ph**
Prints the value on the top of the stack in base 16, without altering the stack. A newline is printed after the value.
## Stack Control
**c**

View File

@ -22,8 +22,8 @@ std::optional<std::string> Evaluate::eval() {
std::optional<std::string> err = std::nullopt;
//
// NUMERICAL OPERATIONS
//
// NUMERICAL OPERATIONS
//
if(val == "+") {
auto math = std::make_unique<Math>(OPType::ADD);
err = math->exec(this->stack);
@ -124,6 +124,24 @@ std::optional<std::string> Evaluate::eval() {
if(err != std::nullopt) {
return err;
}
} else if(val == "pb") { // PRINT TOP ELEMENT IN BASE 2
auto op = std::make_unique<Stack>(OPType::PBB);
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);
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);
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);
err = op->exec(this->stack);
@ -264,8 +282,8 @@ std::optional<std::string> Evaluate::parse_macro(size_t &idx) {
std::optional<std::string> Evaluate::parse_macro_command(std::string val) {
// 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 '!='
// followed by a register name(e.g, >A)
// If command has length equal to three, then it's either '<=', '>=' or '!='
// Check if command is >=, <= or !=
std::string operation = "";
@ -279,9 +297,9 @@ std::optional<std::string> Evaluate::parse_macro_command(std::string val) {
}
// Macro commands works as follow
// 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
// 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);
@ -326,8 +344,8 @@ std::optional<std::string> Evaluate::parse_macro_command(std::string val) {
std::optional<std::string> Evaluate::parse_register_command(std::string val) {
// 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")
// 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') {
// Check if main stack is empty
if(this->stack.empty()) {
@ -353,9 +371,9 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
this->regs[reg_name].stack.push_back(head);
} else if(val.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
// inaccessible(i.e. it follows LIFO policy).
// pushes it onto the stack of selected register.
// The previous value of the register's stack becomes
// inaccessible(i.e. it follows LIFO policy).
// Check if main stack is empty
if(this->stack.empty()) {
@ -379,8 +397,8 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
}
} else if(val.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
// 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);
// Check if register exists
@ -399,8 +417,8 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
this->stack.push_back(value);
} 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
// 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);
// If register does not exists or its stack is empty, push '0' onto the main stack
@ -420,11 +438,11 @@ std::optional<std::string> Evaluate::parse_register_command(std::string val) {
std::optional<std::string> Evaluate::parse_array_command(std::string val) {
// 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')
// 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
// element will be stored in the array indexed by the top-of-stack.
// element will be stored in the array indexed by the top-of-stack.
auto reg_name = val.at(1);
// Check if the main stack has enough elements
@ -459,7 +477,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
// for the array. The selected value, if any, is pushed onto the stack
auto reg_name = val.at(1);
// Check if the main stack is empty
@ -489,7 +507,7 @@ std::optional<std::string> Evaluate::parse_array_command(std::string val) {
return std::string("The array of register '") + reg_name + std::string("' is empty");
}
// Otherwise, use the index to retrieve the array element
// Otherwise, use the index to retrieve the array element
// and to push it onto the main stack
auto reg_it = regs.find(reg_name);
auto arr_it = reg_it->second.array.find(idx);
@ -497,7 +515,7 @@ std::optional<std::string> Evaluate::parse_array_command(std::string val) {
if(arr_it != reg_it->second.array.end()) {
this->stack.push_back(arr_it->second);
} else {
return std::string("Cannot access ") + reg_name +
return std::string("Cannot access ") + reg_name +
std::string("[") + std::to_string(idx) + std::string("]");
}
}

View File

@ -14,7 +14,7 @@ enum class OPType {
ADD, SUB, MUL, DIV, MOD, DIV_MOD, MOD_EXP, EXP,
SQRT, SIN, COS, TAN, FACT, PI, E,
// Stack operations
PCG, P, CLR, PH, SO, DP, PS, CH, CS,
PCG, P, PBB, PBH, PBO, CLR, PH, SO, DP, PS, CH, CS,
// Macro operations
EX, CMP, RI
};

View File

@ -10,6 +10,9 @@ std::optional<std::string> Stack::exec(dc_stack_t &stack) {
switch(this->op_type) {
case OPType::PCG: err = fn_print(stack, true); break;
case OPType::P: err = fn_print(stack, false); break;
case OPType::PBB: err = fn_print_base(stack, Base::BIN); break;
case OPType::PBH: err = fn_print_base(stack, Base::HEX); break;
case OPType::PBO: err = fn_print_base(stack, Base::OCT); break;
case OPType::CLR: stack.clear(); break;
case OPType::PH: fn_pop_head(stack); break;
case OPType::SO: fn_swap_xy(stack); break;
@ -38,6 +41,62 @@ std::optional<std::string> Stack::fn_print(dc_stack_t &stack, bool new_line) {
return std::nullopt;
}
std::optional<std::string> Stack::fn_print_base(dc_stack_t &stack, Base base) {
// Check if the stack is empty
if(stack.empty()) {
return "Cannot print empty stack";
}
// Check if top of the stack is a number
if(!is_num<int>(stack.back())) {
return "This operation requires integers values";
}
// Get top element of the stack
auto head = std::stol(stack.back());
switch(base) {
case Base::BIN: {
// Define a lambda function to convert dec to bin
auto to_bin = [](auto num) -> std::string {
if(num == 0) {
return "0";
}
std::string res = "";
while(num > 0) {
res = (std::to_string(num % 2) + res);
num /= 2;
}
// Remove extra zeros
auto first_one = res.find('1');
if(first_one != std::string::npos) {
res = res.substr(first_one);
}
return res;
};
// Print the number in base 2
std::cout << to_bin(head) << "b" << std::endl;
break;
}
case Base::HEX: {
// Print the number in base 16
std::cout << std::hex << std::uppercase << head << 'h'
<< std::dec << std::nouppercase << std::endl;
break;
}
case Base::OCT: {
// Print the number in base 8
std::cout << std::oct << head << 'o' << std::dec << std::endl;
break;
}
}
return std::nullopt;
}
std::optional<std::string> Stack::fn_pop_head(dc_stack_t &stack) {
// Check if stack is empty
if(stack.empty()) {

View File

@ -2,6 +2,10 @@
#include "operation.h"
enum class Base {
BIN, HEX, OCT
};
class Stack : public IOperation {
public:
Stack(const OPType op_t) : op_type(std::move(op_t)) {}
@ -9,6 +13,7 @@ public:
private:
std::optional<std::string> fn_print(dc_stack_t &stack, bool new_line);
std::optional<std::string> fn_print_base(dc_stack_t &stack, Base base);
std::optional<std::string> fn_pop_head(dc_stack_t &stack);
std::optional<std::string> fn_swap_xy(dc_stack_t &stack);
std::optional<std::string> fn_dup_head(dc_stack_t &stack);