Added 'print_base' function
This commit is contained in:
parent
137c781ee3
commit
f75ac99625
@ -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
16
man.md
@ -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**
|
||||
|
62
src/eval.cpp
62
src/eval.cpp
@ -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("]");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user