Added bitwise operators and fixed various bugs
dc / build (push) Failing after 15s
Details
dc / build (push) Failing after 15s
Details
This commit is contained in:
parent
b247dadbba
commit
c21e9af062
|
@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.12)
|
|||
# Project name and version
|
||||
project(dc VERSION 1.0.4)
|
||||
|
||||
# Retrieve build date
|
||||
string(TIMESTAMP BUILD_DATE "%d-%b-%Y %H:%M:%S")
|
||||
|
||||
# Retrieve git hash
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
|
@ -52,3 +55,4 @@ add_compile_definitions(DC_VER="${PROJECT_VERSION}")
|
|||
add_compile_definitions(DC_HASH="${DC_GIT_HASH}")
|
||||
add_compile_definitions(DC_BUILD="${CMAKE_BUILD_TYPE}")
|
||||
add_compile_definitions(DC_FLAGS="${DC_FLAGS}")
|
||||
add_compile_definitions(DC_BUILD_DATE="${BUILD_DATE}")
|
||||
|
|
15
README.md
15
README.md
|
@ -28,6 +28,7 @@ Some of the supported features are:
|
|||
- Factorial and constants(`!`, `pi`, `e`);
|
||||
- Random number generator(`@`);
|
||||
- Integer conversion(`$`);
|
||||
- Bitwise operations(`{`, `}`, `l`, `L`, `m`, `M`);
|
||||
- Stack operations:
|
||||
- Print top element(`p`, `P`);
|
||||
- Clear the stack(`c`);
|
||||
|
@ -259,7 +260,7 @@ lB -1 * lD + lA # POSITIVE DELTA
|
|||
0 lL x
|
||||
```
|
||||
|
||||
17. Estimate $\pi$ using Monte Carlo simulation
|
||||
17. Estimate $\pi$ using Monte Carlo simulation:
|
||||
```
|
||||
10 k
|
||||
[ 0 1 @ sX 0 1 @ sY ] sR
|
||||
|
@ -273,6 +274,18 @@ lL x 0 ;A lN /
|
|||
4 * p
|
||||
```
|
||||
|
||||
18. Convert an hex color to RGB:
|
||||
```
|
||||
16 i
|
||||
[ Enter hex value: ] P R ? sV
|
||||
lV FF { 0 :A # Blue
|
||||
lV 8 M FF { 1 :A # Green
|
||||
lV 10 M FF { 2 :A # Red
|
||||
[ RED: ] P R 2 ;A p
|
||||
[ GREEN: ] P R 1 ;A p
|
||||
[ RED: ] P R 0 ;A p
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[GPLv3](https://choosealicense.com/licenses/gpl-3.0/)
|
||||
|
|
2
main.cpp
2
main.cpp
|
@ -21,6 +21,7 @@ void helper() {
|
|||
|
||||
void version() {
|
||||
std::cout << "dc (v" << DC_VER << ", " << DC_HASH << ", " << DC_BUILD ")" << std::endl;
|
||||
std::cout << "Build date: " << DC_BUILD_DATE << std::endl;
|
||||
std::cout << "Compile flags: " << DC_FLAGS << std::endl;
|
||||
std::cout << "Copyright (c) 2024 Marco Cetica" << std::endl;
|
||||
std::cout << "License GPLv3+: GNU GPL version 3 or later\n" << std::endl;
|
||||
|
@ -126,6 +127,7 @@ int main(int argc, char **argv) {
|
|||
// Handle errors
|
||||
if(err != std::nullopt) {
|
||||
std::cerr << err.value() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
60
man.md
60
man.md
|
@ -3,7 +3,7 @@ title: dc
|
|||
section: 1
|
||||
header: General Commands Manual
|
||||
footer: Marco Cetica
|
||||
date: March 15, 2024
|
||||
date: March 19, 2024
|
||||
---
|
||||
|
||||
|
||||
|
@ -46,6 +46,23 @@ options(see below).
|
|||
**dc** reads from the standard input, but it can also work with text files using the `-f` flag. Futhermore, you can decide to evaluate an expression
|
||||
without opening the REPL by using the `-e` flag.
|
||||
|
||||
# PROGRAMMING IN DC
|
||||
As a stack-based, concatenative and procedural programming language, **dc** programs follow a *bottom up* approach where the program is built by
|
||||
starting with the most minimal facts about the problem and then is build up towards the complete solution. Following this programming paradigm means
|
||||
creating many short and simple routines that are defined either in terms or existing routines or in terms of built-in primitives.
|
||||
|
||||
The main strength of this paradigm is that you have full control on what happens at the lower levels of your program. This means that your
|
||||
programming logic is not abstracted away into generic protocols, everything you write has to as concrete as possible.
|
||||
Another advantage of this approach is the ability to test and debug interactively each definition and each routines as you build up your solution, without having
|
||||
to rely on external testing framework.
|
||||
Finally, the last major advantage is that dc is very extensible: the core language consists on only a few primitives, which are enough for building solutions
|
||||
and extending the programming language. Furthermore, just as LISP languages, dc does not make any distinctions between code and data, thus it is homoiconic.
|
||||
|
||||
On the other hand, the *bottom up* approach is not suitable for building large infrastructures: the ability to abstract away methods, procedures and to shape
|
||||
real-world objects into templates is essential for many modern programming use cases. As a result, dc excels when used for what it was originally
|
||||
developed for: computations and small math-oriented programs.
|
||||
|
||||
|
||||
# ARCHITECTURE
|
||||
As an advanced scientific calculator, **dc** has a complex architecture defined by the following two data structures:
|
||||
|
||||
|
@ -104,6 +121,7 @@ their invocation. The user can store both numeric and alphanumeric values on the
|
|||
|
||||
Arrays are homogeneous data structures that implement the same data type of the stack, i.e. the string.
|
||||
|
||||
|
||||
# COMMANDS
|
||||
Below, there is a list of supported **dc** commands.
|
||||
|
||||
|
@ -222,6 +240,34 @@ Prints the value on the top of the stack in base 8, without altering the stack.
|
|||
|
||||
Prints the value on the top of the stack in base 16, without altering the stack. A newline is printed after the value.
|
||||
|
||||
## Bitwise Operations
|
||||
**dc** supports various bitwise operations. These operations support integral types only and are represented using
|
||||
a 64bit data type.
|
||||
|
||||
**{**
|
||||
|
||||
Pops two numbers from the stack and computes bitwise *AND* between the second one popped and the first one popped. Pushes the result.
|
||||
|
||||
**}**
|
||||
|
||||
Pops two numbers from the stack and computes bitwise *OR* between the second one popped and the first one popped. Pushes the result.
|
||||
|
||||
**l**
|
||||
|
||||
Pops one **signed integer** from the stack and computes bitwise *NOT*(one's complement). Pushes the result.
|
||||
|
||||
**L**
|
||||
|
||||
Pops two numbers from the stack and computes bitwise *XOR* between the second one popped and the first one popped. Pushes the result.
|
||||
|
||||
**m**
|
||||
|
||||
Pops two numbers from the stack *left shift* the second one popped by *n* bits, where *n* is the first one popped. Pushes the result.
|
||||
|
||||
**M**
|
||||
|
||||
Pops two numbers from the stack *right shift* the second one popped by *n* bits, where *n* is the first one popped. Pushes the result.
|
||||
|
||||
## Stack Control
|
||||
|
||||
**c**
|
||||
|
@ -517,6 +563,18 @@ lL x 0 ;A lN /
|
|||
4 * p
|
||||
```
|
||||
|
||||
14. Convert an hex color to RGB:
|
||||
```
|
||||
16 i
|
||||
[ Enter hex value: ] P R ? sV
|
||||
lV FF { 0 :A # Blue
|
||||
lV 8 M FF { 1 :A # Green
|
||||
lV 10 M FF { 2 :A # Red
|
||||
[ RED: ] P R 2 ;A p
|
||||
[ GREEN: ] P R 1 ;A p
|
||||
[ RED: ] P R 0 ;A p
|
||||
```
|
||||
|
||||
# AUTHORS
|
||||
The original version of the **dc** command was written by Robert Morris and Lorinda Cherry.
|
||||
This version of **dc** is developed by Marco Cetica.
|
||||
|
|
|
@ -4,6 +4,7 @@ set(HEADER_FILES
|
|||
eval.h
|
||||
macro.h
|
||||
mathematics.h
|
||||
bitwise.h
|
||||
operation.h
|
||||
stack.h
|
||||
adt.h
|
||||
|
@ -13,6 +14,7 @@ set(SOURCE_FILES
|
|||
eval.cpp
|
||||
macro.cpp
|
||||
mathematics.cpp
|
||||
bitwise.cpp
|
||||
stack.cpp
|
||||
adt.cpp
|
||||
)
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
#include <bitset>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
|
||||
#include "adt.cpp"
|
||||
#include "bitwise.h"
|
||||
#include "is_num.h"
|
||||
|
||||
std::optional<std::string> Bitwise::exec(dc::Stack<std::string> &stack, dc::Parameters ¶meters, __attribute__((unused)) std::unordered_map<char, dc::Register> ®s) {
|
||||
std::optional<std::string> err = std::nullopt;
|
||||
|
||||
switch(this->op_type) {
|
||||
case OPType::BAND: err = fn_bitwise_and(stack, parameters); break;
|
||||
case OPType::BOR: err = fn_bitwise_or(stack, parameters); break;
|
||||
case OPType::BNOT: err = fn_bitwise_not(stack, parameters); break;
|
||||
case OPType::BXOR: err = fn_bitwise_xor(stack, parameters); break;
|
||||
case OPType::BSL: err = fn_bitwise_lshift(stack, parameters); break;
|
||||
case OPType::BSR: err = fn_bitwise_rshift(stack, parameters); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
std::optional<std::string> Bitwise::fn_bitwise_and(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'{' requires two operands";
|
||||
}
|
||||
|
||||
// Extract two entries from the stack
|
||||
auto len = stack.size() - 1;
|
||||
auto x = stack[len];
|
||||
auto y = stack[len-1];
|
||||
auto is_x_num = is_num<double>(x);
|
||||
auto is_y_num = is_num<double>(y);
|
||||
|
||||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
std::bitset<64> rhs{std::stoul(stack.pop(true))};
|
||||
std::bitset<64> lhs{std::stoul(stack.pop(true))};
|
||||
|
||||
// Compute bitwise AND and push back the result
|
||||
std::bitset<64> result = (lhs & rhs);
|
||||
stack.push(trim_digits(result.to_ullong(), parameters.precision));
|
||||
} else {
|
||||
return "'{' requires numeric values";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Bitwise::fn_bitwise_or(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'}' requires two operands";
|
||||
}
|
||||
|
||||
// Extract two entries from the stack
|
||||
auto len = stack.size() - 1;
|
||||
auto x = stack[len];
|
||||
auto y = stack[len-1];
|
||||
auto is_x_num = is_num<double>(x);
|
||||
auto is_y_num = is_num<double>(y);
|
||||
|
||||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
std::bitset<64> rhs{std::stoul(stack.pop(true))};
|
||||
std::bitset<64> lhs{std::stoul(stack.pop(true))};
|
||||
|
||||
// Compute bitwise AND and push back the result
|
||||
std::bitset<64> result = (lhs | rhs);
|
||||
stack.push(trim_digits(result.to_ullong(), parameters.precision));
|
||||
} else {
|
||||
return "'}' requires numeric values";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::string> Bitwise::fn_bitwise_not(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "'l' requires one operand";
|
||||
}
|
||||
|
||||
// Extract one entry from the stack
|
||||
auto x = stack.pop(false);
|
||||
auto is_x_num = is_num<double>(x);
|
||||
|
||||
// Check whether popped value is a number
|
||||
if(is_x_num) {
|
||||
stack.copy_xyz();
|
||||
// Compute bitwise NOT
|
||||
int result = ~std::stoi(stack.pop(true));
|
||||
stack.push(trim_digits(result, parameters.precision));
|
||||
} else {
|
||||
return "'l' requires numeric values";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::string> Bitwise::fn_bitwise_xor(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'L' requires two operands";
|
||||
}
|
||||
|
||||
// Extract two entries from the stack
|
||||
auto len = stack.size() - 1;
|
||||
auto x = stack[len];
|
||||
auto y = stack[len-1];
|
||||
auto is_x_num = is_num<double>(x);
|
||||
auto is_y_num = is_num<double>(y);
|
||||
|
||||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
std::bitset<64> rhs{std::stoul(stack.pop(true))};
|
||||
std::bitset<64> lhs{std::stoul(stack.pop(true))};
|
||||
|
||||
// Compute bitwise AND and push back the result
|
||||
std::bitset<64> result = (lhs ^ rhs);
|
||||
stack.push(trim_digits(result.to_ullong(), parameters.precision));
|
||||
} else {
|
||||
return "'L' requires numeric values";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::string> Bitwise::fn_bitwise_lshift(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'m' requires two operands";
|
||||
}
|
||||
|
||||
// Extract two entries from the stack
|
||||
auto len = stack.size() - 1;
|
||||
auto x = stack[len];
|
||||
auto y = stack[len-1];
|
||||
auto is_x_num = is_num<double>(x);
|
||||
auto is_y_num = is_num<double>(y);
|
||||
|
||||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
std::bitset<64> pos{std::stoul(stack.pop(true))};
|
||||
std::bitset<64> value{std::stoul(stack.pop(true))};
|
||||
|
||||
// Compute bitwise left shift and push back the result
|
||||
std::bitset<64> result = (value << pos.to_ulong());
|
||||
stack.push(trim_digits(result.to_ullong(), parameters.precision));
|
||||
} else {
|
||||
return "'m' requires numeric values";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Bitwise::fn_bitwise_rshift(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||
// Check if stack has enough elements
|
||||
if(stack.size() < 2) {
|
||||
return "'M' requires two operands";
|
||||
}
|
||||
|
||||
// Extract two entries from the stack
|
||||
auto len = stack.size() - 1;
|
||||
auto x = stack[len];
|
||||
auto y = stack[len-1];
|
||||
auto is_x_num = is_num<double>(x);
|
||||
auto is_y_num = is_num<double>(y);
|
||||
|
||||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
std::bitset<64> pos{std::stoul(stack.pop(true))};
|
||||
std::bitset<64> value{std::stoul(stack.pop(true))};
|
||||
|
||||
// Compute bitwise right shift and push back the result
|
||||
std::bitset<64> result = (value >> pos.to_ulong());
|
||||
stack.push(trim_digits(result.to_ullong(), parameters.precision));
|
||||
} else {
|
||||
return "'M' requires numeric values";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string Bitwise::trim_digits(double number, unsigned int precision) {
|
||||
std::ostringstream oss;
|
||||
|
||||
// Preserve non-zero decimal numbers even when precision is zero
|
||||
if(precision == 0 && std::fmod(number, 1.0) != 0.0) {
|
||||
precision = 2;
|
||||
}
|
||||
|
||||
oss << std::fixed << std::setprecision(static_cast<int>(precision)) << number;
|
||||
std::string s = oss.str();
|
||||
|
||||
return s;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "operation.h"
|
||||
|
||||
class Bitwise : public IOperation {
|
||||
public:
|
||||
explicit Bitwise(const OPType op_t) : op_type(op_t) {}
|
||||
std::optional<std::string> exec(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) override;
|
||||
|
||||
private:
|
||||
std::optional<std::string> fn_bitwise_and(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
std::optional<std::string> fn_bitwise_or(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
std::optional<std::string> fn_bitwise_not(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
std::optional<std::string> fn_bitwise_xor(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
std::optional<std::string> fn_bitwise_lshift(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
std::optional<std::string> fn_bitwise_rshift(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
std::string trim_digits(double number, unsigned int precision);
|
||||
|
||||
OPType op_type;
|
||||
};
|
12
src/eval.cpp
12
src/eval.cpp
|
@ -3,6 +3,7 @@
|
|||
#include "adt.cpp"
|
||||
#include "eval.h"
|
||||
#include "mathematics.h"
|
||||
#include "bitwise.h"
|
||||
#include "stack.h"
|
||||
#include "macro.h"
|
||||
#include "is_num.h"
|
||||
|
@ -42,6 +43,13 @@ void Evaluate::init_environment() {
|
|||
this->op_factory.emplace("e", MAKE_UNIQUE_PTR(Mathematics, OPType::E));
|
||||
this->op_factory.emplace("@", MAKE_UNIQUE_PTR(Mathematics, OPType::RND));
|
||||
this->op_factory.emplace("$", MAKE_UNIQUE_PTR(Mathematics, OPType::INT));
|
||||
// Bitwise operations
|
||||
this->op_factory.emplace("{", MAKE_UNIQUE_PTR(Bitwise, OPType::BAND));
|
||||
this->op_factory.emplace("}", MAKE_UNIQUE_PTR(Bitwise, OPType::BOR));
|
||||
this->op_factory.emplace("l", MAKE_UNIQUE_PTR(Bitwise, OPType::BNOT));
|
||||
this->op_factory.emplace("L", MAKE_UNIQUE_PTR(Bitwise, OPType::BXOR));
|
||||
this->op_factory.emplace("m", MAKE_UNIQUE_PTR(Bitwise, OPType::BSL));
|
||||
this->op_factory.emplace("M", MAKE_UNIQUE_PTR(Bitwise, OPType::BSR));
|
||||
// Stack operations
|
||||
this->op_factory.emplace("p", MAKE_UNIQUE_PTR(Stack, OPType::PCG));
|
||||
this->op_factory.emplace("pb", MAKE_UNIQUE_PTR(Stack, OPType::PBB));
|
||||
|
@ -82,6 +90,8 @@ std::optional<std::string> Evaluate::eval() {
|
|||
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);
|
||||
} else if(token == "q") {
|
||||
std::exit(0);
|
||||
} else if(MACRO_COND(token)) {
|
||||
err = parse_macro(idx);
|
||||
} else if(MACRO_CMD_COND(token)) {
|
||||
|
@ -94,8 +104,6 @@ std::optional<std::string> Evaluate::eval() {
|
|||
err = parse_base_n(token);
|
||||
} else if(is_num<double>(token)) {
|
||||
this->stack.push(token);
|
||||
} else if(token == "q") {
|
||||
std::exit(0);
|
||||
} else {
|
||||
return "Unrecognized option";
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ public:
|
|||
Evaluate(const std::vector<std::string>& e, std::unordered_map<char, dc::Register> &r,
|
||||
dc::Stack<std::string> &s, dc::Parameters &p)
|
||||
: expr(e), regs(r), stack(s), parameters(p) {}
|
||||
Evaluate(std::unordered_map<char, dc::Register> &r, dc::Stack<std::string> &s, dc::Parameters &p)
|
||||
: regs(r), stack(s), parameters(p) {}
|
||||
std::optional<std::string> eval();
|
||||
|
||||
private:
|
||||
|
|
|
@ -158,8 +158,8 @@ std::optional<std::string> Macro::fn_read_input(dc::Stack<std::string> &stack, d
|
|||
}
|
||||
|
||||
// Push the input onto the main stack and execute it as a macro
|
||||
stack.push(user_input);
|
||||
Evaluate evaluator(regs, stack, parameters);
|
||||
std::vector<std::string> tokens = split(user_input);
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
|
||||
auto err = evaluator.eval();
|
||||
if(err != std::nullopt) {
|
||||
|
|
|
@ -53,8 +53,8 @@ std::optional<std::string> Mathematics::fn_add(dc::Stack<std::string> &stack, co
|
|||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
auto lhs = std::stod(stack.pop(true));
|
||||
auto rhs = std::stod(stack.pop(true));
|
||||
auto lhs = std::stod(stack.pop(true));
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push(trim_digits((lhs + rhs), parameters.precision));
|
||||
|
@ -81,11 +81,11 @@ std::optional<std::string> Mathematics::fn_sub(dc::Stack<std::string> &stack, co
|
|||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
auto lhs = std::stod(stack.pop(true));
|
||||
auto rhs = std::stod(stack.pop(true));
|
||||
auto lhs = std::stod(stack.pop(true));
|
||||
|
||||
// Subtract the two operands
|
||||
auto result = (-(lhs - rhs));
|
||||
auto result = (lhs - rhs);
|
||||
|
||||
// Compare the result with an epsilon value
|
||||
// to prevent -0/+0 results
|
||||
|
@ -119,8 +119,8 @@ std::optional<std::string> Mathematics::fn_mul(dc::Stack<std::string> &stack, co
|
|||
// Check whether both entries are numbers
|
||||
if(is_x_num && is_y_num) {
|
||||
stack.copy_xyz();
|
||||
auto lhs = std::stod(stack.pop(true));
|
||||
auto rhs = std::stod(stack.pop(true));
|
||||
auto lhs = std::stod(stack.pop(true));
|
||||
|
||||
// Push back the result as a string
|
||||
stack.push(trim_digits((lhs * rhs), parameters.precision));
|
||||
|
@ -253,9 +253,9 @@ std::optional<std::string> Mathematics::fn_mod_exp(dc::Stack<std::string> &stack
|
|||
// c ≡ b^e (mod n)
|
||||
if(is_n_num && is_e_num && is_b_num) {
|
||||
stack.copy_xyz();
|
||||
auto modulus = std::stoi(stack.pop(true));
|
||||
auto exponent = std::stoi(stack.pop(true));
|
||||
auto base = std::stoi(stack.pop(true));
|
||||
auto modulus = std::stol(stack.pop(true));
|
||||
auto exponent = std::stol(stack.pop(true));
|
||||
auto base = std::stol(stack.pop(true));
|
||||
|
||||
if(modulus == 1) {
|
||||
stack.push("0");
|
||||
|
|
|
@ -14,6 +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, RND, INT,
|
||||
// Bitwise operations
|
||||
BAND, BOR, BNOT, BXOR, BSL, BSR,
|
||||
// Stack operations
|
||||
PCG, P, PBB, PBH, PBO, CLR, PH, SO, DP, PS, CH, CS,
|
||||
SP, GP, SOR, GOR, SIR, GIR, LX, LY, LZ,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <iostream>
|
||||
#include <bitset>
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
|
@ -67,8 +68,10 @@ std::optional<std::string> Stack::fn_print(dc::Stack<std::string> &stack, dc::Pa
|
|||
break;
|
||||
}
|
||||
case dc::radix_base::BIN: {
|
||||
auto head = std::stol(stack.pop(false));
|
||||
std::cout << to_bin(head) << 'b' << std::endl;
|
||||
std::bitset<64> head{std::stoul(stack.pop(false))};
|
||||
auto bin_value = bin_prettify(head.to_string());
|
||||
|
||||
std::cout << bin_value << std::endl;
|
||||
break;
|
||||
}
|
||||
case dc::radix_base::OCT: {
|
||||
|
@ -141,7 +144,10 @@ std::optional<std::string> Stack::fn_print_stack(const dc::Stack<std::string> &s
|
|||
}
|
||||
case dc::radix_base::BIN: {
|
||||
for(const auto& it : std::ranges::reverse_view(const_ref)) {
|
||||
std::cout << to_bin(std::stol(it)) << 'b' << std::endl;
|
||||
std::bitset<64> head{std::stoul(it)};
|
||||
auto bin_value = bin_prettify(head.to_string());
|
||||
|
||||
std::cout << bin_value << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -313,22 +319,22 @@ std::optional<std::string> Stack::fn_get_lastz(dc::Stack<std::string> &stack) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr std::string Stack::to_bin(auto num) {
|
||||
if(num == 0) {
|
||||
return "0";
|
||||
constexpr std::string Stack::bin_prettify(std::string s) {
|
||||
std::string result;
|
||||
|
||||
// Find the first '1' on the binary string
|
||||
auto pos = s.find('1');
|
||||
|
||||
if(pos != std::string::npos) {
|
||||
// Take only the significant part of the number
|
||||
result = s.substr(pos);
|
||||
} else {
|
||||
// Otherwise the number is 'zero'
|
||||
result = '0';
|
||||
}
|
||||
|
||||
std::string res;
|
||||
while(num > 0) {
|
||||
res = (std::to_string(num % 2) + res);
|
||||
num /= 2;
|
||||
}
|
||||
// Add 'b' suffix
|
||||
result.push_back('b');
|
||||
|
||||
// Remove extra zeros
|
||||
auto first_one = res.find('1');
|
||||
if(first_one != std::string::npos) {
|
||||
res = res.substr(first_one);
|
||||
}
|
||||
|
||||
return res;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ private:
|
|||
std::optional<std::string> fn_get_lastx(dc::Stack<std::string> &stack);
|
||||
std::optional<std::string> fn_get_lasty(dc::Stack<std::string> &stack);
|
||||
std::optional<std::string> fn_get_lastz(dc::Stack<std::string> &stack);
|
||||
constexpr std::string to_bin(auto num);
|
||||
constexpr std::string bin_prettify(std::string s);
|
||||
|
||||
OPType op_type;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
utest() {
|
||||
PROGRAM="$PWD/build/dc"
|
||||
EXPECTED="10b"
|
||||
ACTUAL=$("$PROGRAM" -e '2 d i o 1010 0010 { p')
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test empty stack
|
||||
EXPECTED="'{' requires two operands"
|
||||
ACTUAL=$("$PROGRAM" -e '{' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test non numerical values
|
||||
EXPECTED="'{' requires numeric values"
|
||||
ACTUAL=$("$PROGRAM" -e '5 [ foo ] {' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
}
|
||||
# vim: ts=4 sw=4 softtabstop=4 expandtab:
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
utest() {
|
||||
PROGRAM="$PWD/build/dc"
|
||||
EXPECTED="1111111111111111111111111111111111111111111111111111111111111101b"
|
||||
ACTUAL=$("$PROGRAM" -e '2 d i o 10 l p')
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test empty stack
|
||||
EXPECTED="'l' requires one operand"
|
||||
ACTUAL=$("$PROGRAM" -e 'l' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test non numerical values
|
||||
EXPECTED="'l' requires numeric values"
|
||||
ACTUAL=$("$PROGRAM" -e '[ foo ] l' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
}
|
||||
# vim: ts=4 sw=4 softtabstop=4 expandtab:
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
utest() {
|
||||
PROGRAM="$PWD/build/dc"
|
||||
EXPECTED="1011b"
|
||||
ACTUAL=$("$PROGRAM" -e '2 d i o 1000 0011 } p')
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test empty stack
|
||||
EXPECTED="'}' requires two operands"
|
||||
ACTUAL=$("$PROGRAM" -e '}' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test non numerical values
|
||||
EXPECTED="'}' requires numeric values"
|
||||
ACTUAL=$("$PROGRAM" -e '5 [ foo ] }' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
}
|
||||
# vim: ts=4 sw=4 softtabstop=4 expandtab:
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
utest() {
|
||||
PROGRAM="$PWD/build/dc"
|
||||
EXPECTED="0b"
|
||||
ACTUAL=$("$PROGRAM" -e '2 d i o 10 10 L p')
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test empty stack
|
||||
EXPECTED="'L' requires two operands"
|
||||
ACTUAL=$("$PROGRAM" -e 'L' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test non numerical values
|
||||
EXPECTED="'L' requires numeric values"
|
||||
ACTUAL=$("$PROGRAM" -e '5 [ foo ] L' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
}
|
||||
# vim: ts=4 sw=4 softtabstop=4 expandtab:
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
utest() {
|
||||
PROGRAM="$PWD/build/dc"
|
||||
EXPECTED="101000b" # 40
|
||||
ACTUAL=$("$PROGRAM" -e '2 d i o 1010 10 m p') # 10 * 2^2
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test empty stack
|
||||
EXPECTED="'m' requires two operands"
|
||||
ACTUAL=$("$PROGRAM" -e 'm' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test non numerical values
|
||||
EXPECTED="'m' requires numeric values"
|
||||
ACTUAL=$("$PROGRAM" -e '5 [ foo ] m' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
}
|
||||
# vim: ts=4 sw=4 softtabstop=4 expandtab:
|
|
@ -32,6 +32,11 @@ test_print_bin() {
|
|||
EXPECTED="This output radix requires integer values"
|
||||
ACTUAL=$("$PROGRAM" -e '[ foo ] pb' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test negative number
|
||||
EXPECTED="1111111111111111111111111111111111111111111111111111111111110001b"
|
||||
ACTUAL=$("$PROGRAM" -e "-15 pb")
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
}
|
||||
|
||||
test_print_hex() {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
utest() {
|
||||
PROGRAM="$PWD/build/dc"
|
||||
EXPECTED="100000b" # 32
|
||||
ACTUAL=$("$PROGRAM" -e '2 d i o 10000000000 101 M p') # 1024 / 2^5
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test empty stack
|
||||
EXPECTED="'M' requires two operands"
|
||||
ACTUAL=$("$PROGRAM" -e 'M' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test non numerical values
|
||||
EXPECTED="'M' requires numeric values"
|
||||
ACTUAL=$("$PROGRAM" -e '5 [ foo ] M' 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
}
|
||||
# vim: ts=4 sw=4 softtabstop=4 expandtab:
|
Loading…
Reference in New Issue