Added bitwise operators and fixed various bugs
dc / build (push) Failing after 15s Details

This commit is contained in:
Marco Cetica 2024-03-19 12:22:01 +01:00
parent b247dadbba
commit c21e9af062
Signed by: marco
GPG Key ID: 45060A949E90D0FD
21 changed files with 468 additions and 34 deletions

View File

@ -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}")

View File

@ -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/)

View File

@ -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
View File

@ -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.

View File

@ -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
)

202
src/bitwise.cpp Normal file
View 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 &parameters, __attribute__((unused)) std::unordered_map<char, dc::Register> &regs) {
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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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 &parameters) {
// 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
View 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 &parameters, std::unordered_map<char, dc::Register> &regs) override;
private:
std::optional<std::string> fn_bitwise_and(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
std::optional<std::string> fn_bitwise_or(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
std::optional<std::string> fn_bitwise_not(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
std::optional<std::string> fn_bitwise_xor(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
std::optional<std::string> fn_bitwise_lshift(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
std::optional<std::string> fn_bitwise_rshift(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
std::string trim_digits(double number, unsigned int precision);
OPType op_type;
};

View File

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

View File

@ -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:

View File

@ -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) {

View File

@ -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");

View File

@ -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,

View File

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

View File

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

19
tests/test_band Normal file
View 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
View 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
View 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
View 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
View 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:

View File

@ -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() {

19
tests/test_rshift Normal file
View 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: