Added bitwise operators and fixed various bugs
All checks were successful
dc / build (push) Successful in 15s
All checks were successful
dc / build (push) Successful in 15s
This commit is contained in:
parent
b247dadbba
commit
62ad335e58
@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.12)
|
|||||||
# Project name and version
|
# Project name and version
|
||||||
project(dc VERSION 1.0.4)
|
project(dc VERSION 1.0.4)
|
||||||
|
|
||||||
|
# Retrieve build date
|
||||||
|
string(TIMESTAMP BUILD_DATE "%d-%b-%Y %H:%M:%S")
|
||||||
|
|
||||||
# Retrieve git hash
|
# Retrieve git hash
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND git rev-parse --short HEAD
|
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_HASH="${DC_GIT_HASH}")
|
||||||
add_compile_definitions(DC_BUILD="${CMAKE_BUILD_TYPE}")
|
add_compile_definitions(DC_BUILD="${CMAKE_BUILD_TYPE}")
|
||||||
add_compile_definitions(DC_FLAGS="${DC_FLAGS}")
|
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`);
|
- Factorial and constants(`!`, `pi`, `e`);
|
||||||
- Random number generator(`@`);
|
- Random number generator(`@`);
|
||||||
- Integer conversion(`$`);
|
- Integer conversion(`$`);
|
||||||
|
- Bitwise operations(`{`, `}`, `l`, `L`, `m`, `M`);
|
||||||
- Stack operations:
|
- Stack operations:
|
||||||
- Print top element(`p`, `P`);
|
- Print top element(`p`, `P`);
|
||||||
- Clear the stack(`c`);
|
- Clear the stack(`c`);
|
||||||
@ -259,7 +260,7 @@ lB -1 * lD + lA # POSITIVE DELTA
|
|||||||
0 lL x
|
0 lL x
|
||||||
```
|
```
|
||||||
|
|
||||||
17. Estimate $\pi$ using Monte Carlo simulation
|
17. Estimate $\pi$ using Monte Carlo simulation:
|
||||||
```
|
```
|
||||||
10 k
|
10 k
|
||||||
[ 0 1 @ sX 0 1 @ sY ] sR
|
[ 0 1 @ sX 0 1 @ sY ] sR
|
||||||
@ -273,6 +274,18 @@ lL x 0 ;A lN /
|
|||||||
4 * p
|
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
|
## License
|
||||||
|
|
||||||
[GPLv3](https://choosealicense.com/licenses/gpl-3.0/)
|
[GPLv3](https://choosealicense.com/licenses/gpl-3.0/)
|
||||||
|
2
main.cpp
2
main.cpp
@ -21,6 +21,7 @@ void helper() {
|
|||||||
|
|
||||||
void version() {
|
void version() {
|
||||||
std::cout << "dc (v" << DC_VER << ", " << DC_HASH << ", " << DC_BUILD ")" << std::endl;
|
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 << "Compile flags: " << DC_FLAGS << std::endl;
|
||||||
std::cout << "Copyright (c) 2024 Marco Cetica" << std::endl;
|
std::cout << "Copyright (c) 2024 Marco Cetica" << std::endl;
|
||||||
std::cout << "License GPLv3+: GNU GPL version 3 or later\n" << 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
|
// Handle errors
|
||||||
if(err != std::nullopt) {
|
if(err != std::nullopt) {
|
||||||
std::cerr << err.value() << std::endl;
|
std::cerr << err.value() << std::endl;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
60
man.md
60
man.md
@ -3,7 +3,7 @@ title: dc
|
|||||||
section: 1
|
section: 1
|
||||||
header: General Commands Manual
|
header: General Commands Manual
|
||||||
footer: Marco Cetica
|
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
|
**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.
|
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
|
# ARCHITECTURE
|
||||||
As an advanced scientific calculator, **dc** has a complex architecture defined by the following two data structures:
|
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.
|
Arrays are homogeneous data structures that implement the same data type of the stack, i.e. the string.
|
||||||
|
|
||||||
|
|
||||||
# COMMANDS
|
# COMMANDS
|
||||||
Below, there is a list of supported **dc** 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.
|
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
|
## Stack Control
|
||||||
|
|
||||||
**c**
|
**c**
|
||||||
@ -517,6 +563,18 @@ lL x 0 ;A lN /
|
|||||||
4 * p
|
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
|
# AUTHORS
|
||||||
The original version of the **dc** command was written by Robert Morris and Lorinda Cherry.
|
The original version of the **dc** command was written by Robert Morris and Lorinda Cherry.
|
||||||
This version of **dc** is developed by Marco Cetica.
|
This version of **dc** is developed by Marco Cetica.
|
||||||
|
@ -4,6 +4,7 @@ set(HEADER_FILES
|
|||||||
eval.h
|
eval.h
|
||||||
macro.h
|
macro.h
|
||||||
mathematics.h
|
mathematics.h
|
||||||
|
bitwise.h
|
||||||
operation.h
|
operation.h
|
||||||
stack.h
|
stack.h
|
||||||
adt.h
|
adt.h
|
||||||
@ -13,6 +14,7 @@ set(SOURCE_FILES
|
|||||||
eval.cpp
|
eval.cpp
|
||||||
macro.cpp
|
macro.cpp
|
||||||
mathematics.cpp
|
mathematics.cpp
|
||||||
|
bitwise.cpp
|
||||||
stack.cpp
|
stack.cpp
|
||||||
adt.cpp
|
adt.cpp
|
||||||
)
|
)
|
||||||
|
202
src/bitwise.cpp
Normal file
202
src/bitwise.cpp
Normal file
@ -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;
|
||||||
|
}
|
20
src/bitwise.h
Normal file
20
src/bitwise.h
Normal file
@ -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 "adt.cpp"
|
||||||
#include "eval.h"
|
#include "eval.h"
|
||||||
#include "mathematics.h"
|
#include "mathematics.h"
|
||||||
|
#include "bitwise.h"
|
||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "is_num.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("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::RND));
|
||||||
this->op_factory.emplace("$", MAKE_UNIQUE_PTR(Mathematics, OPType::INT));
|
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
|
// Stack operations
|
||||||
this->op_factory.emplace("p", MAKE_UNIQUE_PTR(Stack, OPType::PCG));
|
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("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()) {
|
if (this->op_factory.find(token) != this->op_factory.end()) {
|
||||||
std::unique_ptr<IOperation> operation = this->op_factory[token]();
|
std::unique_ptr<IOperation> operation = this->op_factory[token]();
|
||||||
err = operation->exec(this->stack, this->parameters, this->regs);
|
err = operation->exec(this->stack, this->parameters, this->regs);
|
||||||
|
} else if(token == "q") {
|
||||||
|
std::exit(0);
|
||||||
} else if(MACRO_COND(token)) {
|
} else if(MACRO_COND(token)) {
|
||||||
err = parse_macro(idx);
|
err = parse_macro(idx);
|
||||||
} else if(MACRO_CMD_COND(token)) {
|
} else if(MACRO_CMD_COND(token)) {
|
||||||
@ -94,8 +104,6 @@ std::optional<std::string> Evaluate::eval() {
|
|||||||
err = parse_base_n(token);
|
err = parse_base_n(token);
|
||||||
} else if(is_num<double>(token)) {
|
} else if(is_num<double>(token)) {
|
||||||
this->stack.push(token);
|
this->stack.push(token);
|
||||||
} else if(token == "q") {
|
|
||||||
std::exit(0);
|
|
||||||
} else {
|
} else {
|
||||||
return "Unrecognized option";
|
return "Unrecognized option";
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,6 @@ public:
|
|||||||
Evaluate(const std::vector<std::string>& e, std::unordered_map<char, dc::Register> &r,
|
Evaluate(const std::vector<std::string>& e, std::unordered_map<char, dc::Register> &r,
|
||||||
dc::Stack<std::string> &s, dc::Parameters &p)
|
dc::Stack<std::string> &s, dc::Parameters &p)
|
||||||
: expr(e), regs(r), stack(s), 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();
|
std::optional<std::string> eval();
|
||||||
|
|
||||||
private:
|
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
|
// Push the input onto the main stack and execute it as a macro
|
||||||
stack.push(user_input);
|
std::vector<std::string> tokens = split(user_input);
|
||||||
Evaluate evaluator(regs, stack, parameters);
|
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||||
|
|
||||||
auto err = evaluator.eval();
|
auto err = evaluator.eval();
|
||||||
if(err != std::nullopt) {
|
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
|
// Check whether both entries are numbers
|
||||||
if(is_x_num && is_y_num) {
|
if(is_x_num && is_y_num) {
|
||||||
stack.copy_xyz();
|
stack.copy_xyz();
|
||||||
auto lhs = std::stod(stack.pop(true));
|
|
||||||
auto rhs = 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
|
// Push back the result as a string
|
||||||
stack.push(trim_digits((lhs + rhs), parameters.precision));
|
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
|
// Check whether both entries are numbers
|
||||||
if(is_x_num && is_y_num) {
|
if(is_x_num && is_y_num) {
|
||||||
stack.copy_xyz();
|
stack.copy_xyz();
|
||||||
auto lhs = std::stod(stack.pop(true));
|
|
||||||
auto rhs = std::stod(stack.pop(true));
|
auto rhs = std::stod(stack.pop(true));
|
||||||
|
auto lhs = std::stod(stack.pop(true));
|
||||||
|
|
||||||
// Subtract the two operands
|
// Subtract the two operands
|
||||||
auto result = (-(lhs - rhs));
|
auto result = (lhs - rhs);
|
||||||
|
|
||||||
// Compare the result with an epsilon value
|
// Compare the result with an epsilon value
|
||||||
// to prevent -0/+0 results
|
// 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
|
// Check whether both entries are numbers
|
||||||
if(is_x_num && is_y_num) {
|
if(is_x_num && is_y_num) {
|
||||||
stack.copy_xyz();
|
stack.copy_xyz();
|
||||||
auto lhs = std::stod(stack.pop(true));
|
|
||||||
auto rhs = 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
|
// Push back the result as a string
|
||||||
stack.push(trim_digits((lhs * rhs), parameters.precision));
|
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)
|
// c ≡ b^e (mod n)
|
||||||
if(is_n_num && is_e_num && is_b_num) {
|
if(is_n_num && is_e_num && is_b_num) {
|
||||||
stack.copy_xyz();
|
stack.copy_xyz();
|
||||||
auto modulus = std::stoi(stack.pop(true));
|
auto modulus = std::stol(stack.pop(true));
|
||||||
auto exponent = std::stoi(stack.pop(true));
|
auto exponent = std::stol(stack.pop(true));
|
||||||
auto base = std::stoi(stack.pop(true));
|
auto base = std::stol(stack.pop(true));
|
||||||
|
|
||||||
if(modulus == 1) {
|
if(modulus == 1) {
|
||||||
stack.push("0");
|
stack.push("0");
|
||||||
|
@ -14,6 +14,8 @@ enum class OPType {
|
|||||||
ADD, SUB, MUL, DIV, MOD, DIV_MOD, MOD_EXP, EXP,
|
ADD, SUB, MUL, DIV, MOD, DIV_MOD, MOD_EXP, EXP,
|
||||||
SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN, FACT, PI,
|
SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN, FACT, PI,
|
||||||
E, RND, INT,
|
E, RND, INT,
|
||||||
|
// Bitwise operations
|
||||||
|
BAND, BOR, BNOT, BXOR, BSL, BSR,
|
||||||
// Stack operations
|
// 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, LX, LY, LZ,
|
SP, GP, SOR, GOR, SIR, GIR, LX, LY, LZ,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <bitset>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
@ -67,8 +68,10 @@ std::optional<std::string> Stack::fn_print(dc::Stack<std::string> &stack, dc::Pa
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case dc::radix_base::BIN: {
|
case dc::radix_base::BIN: {
|
||||||
auto head = std::stol(stack.pop(false));
|
std::bitset<64> head{std::stoul(stack.pop(false))};
|
||||||
std::cout << to_bin(head) << 'b' << std::endl;
|
auto bin_value = bin_prettify(head.to_string());
|
||||||
|
|
||||||
|
std::cout << bin_value << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case dc::radix_base::OCT: {
|
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: {
|
case dc::radix_base::BIN: {
|
||||||
for(const auto& it : std::ranges::reverse_view(const_ref)) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@ -313,22 +319,22 @@ std::optional<std::string> Stack::fn_get_lastz(dc::Stack<std::string> &stack) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::string Stack::to_bin(auto num) {
|
std::string Stack::bin_prettify(std::string s) {
|
||||||
if(num == 0) {
|
std::string result;
|
||||||
return "0";
|
|
||||||
|
// 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;
|
// Add 'b' suffix
|
||||||
while(num > 0) {
|
result.push_back('b');
|
||||||
res = (std::to_string(num % 2) + res);
|
|
||||||
num /= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove extra zeros
|
return result;
|
||||||
auto first_one = res.find('1');
|
|
||||||
if(first_one != std::string::npos) {
|
|
||||||
res = res.substr(first_one);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ private:
|
|||||||
std::optional<std::string> fn_get_lastx(dc::Stack<std::string> &stack);
|
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_lasty(dc::Stack<std::string> &stack);
|
||||||
std::optional<std::string> fn_get_lastz(dc::Stack<std::string> &stack);
|
std::optional<std::string> fn_get_lastz(dc::Stack<std::string> &stack);
|
||||||
constexpr std::string to_bin(auto num);
|
std::string bin_prettify(std::string s);
|
||||||
|
|
||||||
OPType op_type;
|
OPType op_type;
|
||||||
};
|
};
|
||||||
|
19
tests/test_band
Normal file
19
tests/test_band
Normal file
@ -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:
|
19
tests/test_bnot
Normal file
19
tests/test_bnot
Normal file
@ -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:
|
19
tests/test_bor
Normal file
19
tests/test_bor
Normal file
@ -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:
|
19
tests/test_bxor
Normal file
19
tests/test_bxor
Normal file
@ -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:
|
19
tests/test_lshift
Normal file
19
tests/test_lshift
Normal file
@ -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"
|
EXPECTED="This output radix requires integer values"
|
||||||
ACTUAL=$("$PROGRAM" -e '[ foo ] pb' 2>&1) || true
|
ACTUAL=$("$PROGRAM" -e '[ foo ] pb' 2>&1) || true
|
||||||
assert_eq "$EXPECTED" "$ACTUAL"
|
assert_eq "$EXPECTED" "$ACTUAL"
|
||||||
|
|
||||||
|
# Test negative number
|
||||||
|
EXPECTED="1111111111111111111111111111111111111111111111111111111111110001b"
|
||||||
|
ACTUAL=$("$PROGRAM" -e "-15 pb")
|
||||||
|
assert_eq "$EXPECTED" "$ACTUAL"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_print_hex() {
|
test_print_hex() {
|
||||||
|
19
tests/test_rshift
Normal file
19
tests/test_rshift
Normal file
@ -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
Block a user