Compare commits
24 Commits
factory_pa
...
master
Author  SHA1  Date  

6b0aca61f0  
9d5aeebef3  
f3a83580e8  
711080e27c  
a8bbeece41  
89b7825885  
19ceac3e88  
5de860a089  
5997f06bd0  
616de523cf  
ef2709b54b  
f5afd4019b  
62ad335e58  
b247dadbba  
d0156fae3a  
0e15856762  
4a71a91ee8  
dbecebce07  
de558064c8  
3c257a1fd1  
674d6deef6  
aebfec4fb3  
4b533331f4  
324503d320 
@ 1,7 +1,7 @@


name: dc


on:


push:


branches: [master, factory_parsing]


branches: [master]


workflow_dispatch:




jobs:


@ 10,12 +10,13 @@ jobs:


steps:


 name: Checkout repo


uses: actions/checkout@main


 name: Build dc


 name: Install dependencies


run: 


apt y update && apt y install cmake buildessential


mkdir build && cd build


cmake ..


make


apt y update && apt y install cmake ninjabuild buildessential


 name: Build DC


run: 


cmake B build G Ninja DCMAKE_BUILD_TYPE=Release .


cmake build build


 name: Run unit tests


run: 


./utest.sh tests



10
.github/workflows/dc.yml
vendored
10
.github/workflows/dc.yml
vendored
@ 10,11 +10,13 @@ jobs:


steps:


 name: Checkout repo


uses: actions/checkout@main


 name: Build dc


 name: Install dependencies


run: 


mkdir build && cd build


cmake ..


make


sudo apt y update && sudo apt y install cmake ninjabuild buildessential


 name: Build DC


run: 


cmake B build G Ninja DCMAKE_BUILD_TYPE=Release .


cmake build build


 name: Run unit tests


run: 


./utest.sh tests



@ 1,16 +1,58 @@


cmake_minimum_required(VERSION 3.12)


project(dc)




# Project name and version


project(dc VERSION 1.0.6)




# Retrieve build date


string(TIMESTAMP BUILD_DATE "%d%b%Y %H:%M:%S")




# Retrieve git hash


execute_process(


COMMAND git revparse short HEAD


WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}


OUTPUT_VARIABLE DC_GIT_HASH


OUTPUT_STRIP_TRAILING_WHITESPACE


)




# Set C++ version


set(CMAKE_CXX_STANDARD 20)


set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \


Wall Wextra Werror pedanticerrors \




# Set debug flags by default


if(NOT CMAKE_BUILD_TYPE)


set(CMAKE_BUILD_TYPE Debug)


endif()




# Debug build flags


set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \


Wall Wextra pedantic Werror \


fstackprotectorstrong D_FORTIFY_SOURCE=2 \


Wformatsecurity fsanitize=address fsanitize=undefined \


fstackclashprotection Wundef Wshadow Wpointerarith \


Wcastalign Wwritestrings ftrapv std=c++20 O3")


Wcastalign Wwritestrings ftrapv std=c++20 g O2")




# Release build flags


set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} O3 std=c++20")




# Get build flags


if(CMAKE_BUILD_TYPE STREQUAL "Debug")


set(DC_FLAGS "${CMAKE_CXX_FLAGS_DEBUG}")


elseif(CMAKE_BUILD_TYPE STREQUAL "Release")


set(DC_FLAGS "${CMAKE_CXX_FLAGS_RELEASE}")


endif()




# Main file


add_executable(dc main.cpp)




# Source files


include_directories(src)


add_subdirectory(src)




# Link sources to main


target_link_libraries(dc src)




# Pass variables to the preprocessor


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



103
README.md
103
README.md
@ 1,14 +1,14 @@


# dc ![](https://github.com/ceticamarco/dc/actions/workflows/dc.yml/badge.svg)


# dc ![](https://github.com/ceticamarco/dc/actions/workflows/dc.yml/badge.svg) [![CodeFactor](https://www.codefactor.io/repository/github/ceticamarco/dc/badge/master)](https://www.codefactor.io/repository/github/ceticamarco/dc/overview/master)




**dc** is an advanced, scientific and programmable RPN desktop calculator with macro support (re)written in C++.


By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions.


Its capabilities can be further extended by writing userdefined programs using the embedded, turingcomplete, macro system.


By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions and


its capabilities can be further extended by writing userdefined programs using the embedded, turingcomplete, macro system.




**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. Furthermore, you can decide to evaluate an expression


without opening the REPL by using the `e` flag.




Operands are pushed onto the stack following the LIFO policy; operators, on the other hand, pop one or more values


from the stack and push back the result. By default **dc** is very quiet, in order to inquiry the stack you need to use one of the supported


from the stack and push back the result. By default, **dc** is very quiet, in order to inquiry the stack you need to use one of the supported


options(see below).




`dc` can be invoked with the following command line options:


@ 23,16 +23,23 @@ RPN desktop calculator with macro support. Usage:


Some of the supported features are:


 Basic arithmetical operations(`+`, ``, `*`, `/`, `^`, `%`);


 Scientific notation support(`5e3` > `5000`);


 Support for complex numbers(`1 v` > `(0,1)`);


 Trigonometrical functions(`sin`, `cos`, `tan`, `asin`, `acos`, `atan`);


 Base10 logarithm(`y`);


 Statistical functions(permutations, combinations, summation, sum of squares, mean, standard deviation, linear regression);


 Base conversion(binary: `pb`, octal: `po`, hexadecimal: `px`);


 Factorial and constants(`!`, `pi`, `e`);


 Random number generator(`@`);


 Integer conversion(`$`);


 Bitwise operations(`{`, `}`, `l`, `L`, `m`, `M`);


 Stack operations:


 Print top element(`p`, `P`);


 Print top element(`p`, `P`, `p.`);


 Clear the stack(`c`);


 Remove top element(`R`);


 Swap order of top two elements(`r`);


 Duplicate top element(`d`);


 Dump the whole stack(`f`);


 Last head, 2nd, 3rd element of the stack(`.x`, `.y`, `.z`);


 Parameters:


 Set precision(`k`);


 Set input and output radix(`i` and `o`);


@ 45,18 +52,19 @@ Some of the supported features are:


 Macros:


 Define a new macro inside square brackets(`[ ]`);


 Executing a macro from the stack(`x`);


 Evaluate a macro by comparing topofhead and secondofhead elements(`>X`, `<X`, `>=X`, `<=X`, `!=` where `X` is a register).


 Evaluate a macro by comparing topofhead and secondofhead elements(`>X`, `<X`, `>=X`, `<=X`, `!=` where `X` is a register);


 Load external file(`'`).




And much more. You can find the complete manual [here](https://github.com/icebit/dc/blob/master/man.md).




## Installation


`dc` is written in C++20 without using any additional dependency. In order to build it, install a recent version of CMake and issue


the following command:


`dc` is written in C++20 without using any additional dependency. In order to build it, install [CMake](https://cmake.org/), [Ninja](https://ninjabuild.org/)


and issue the following commands:


```sh


$> mkdir build && cd build


$> cmake .. && make


$> cmake B build G Ninja DCMAKE_BUILD_TYPE=Release .


$> cmake build build


```


A new staticallycompiled binary called `dc` will be created in your local folder. To generate a man page from the `man.md` document,


A new staticallycompiled binary called `dc` will be created in the `build/` directory. To generate a man page from the `man.md` document,


use the following command(note: needs pandoc):


```sh


$> pandoc man.md s t man > dc.1


@ 151,7 +159,15 @@ lA lB # Push 'A' and 'B' content onto the stack


p # Print top element(output: 5)


```




7. Print out numbers from 1 through userdefined upper bound:


7. Recall last value of top three elements of the stack:


```sh


10 1 / # < Wrong operation: division instead of sum


.y # Call last value of y register


.x # Call last value of x register


+ p # Sum and print head(output: 11)


```




8. Print out numbers from 1 through userdefined upper bound:


```sh


[ p 1 + d lN >L ] sL # Print numbers from 1 through 'N'




@ 160,20 +176,20 @@ p # Print top element(output: 5)


c 1 lL x # Clear the stack, add lower bound, load and execute macro


```




8. Sum the first 36 natural numbers(ðŸ˜ˆ), i.e.,


9. Sum the first 36 natural numbers(ðŸ˜ˆ), i.e.,


$$\sum_{i=1}^{37} i = 666$$


```sh


$> dc e "36 [ d 1  d 1 <F + ] d sF x p"


```




5. Print the first 20 values of `n!`:


10. Print the first 20 values of `n!`:


```


[ la 1 + d sa * p la 20 >y ] sy


0 sa 1


ly x


```




9. Compute the factorial of a given number:


11. Compute the factorial of a given number:


```


[ ln 1  sn ln la * sa ln 1 !=f ] sf


[ Enter value: ] P ? sn


@ 182,7 +198,7 @@ lf x


la p


```




10. Compute the sum $8AB6F + B783E$ in base 16. Print the result in base 10 and in base 2:


12. Compute the sum $8AB6F + B783E$ in base 16. Print the result in base 10 and in base 2:


```


16 i


8AB6F B783E +


@ 190,7 +206,7 @@ la p


[ Result in base 2: ] P R pb


```




11. Compute the Greatest Common Divisor(GCD) between two userdefined numbers `A` and `B`:


13. Compute the Greatest Common Divisor(GCD) between two userdefined numbers `A` and `B`:


```


[ Enter A: ] P R ?


[ Enter B: ] P R ?


@ 198,7 +214,7 @@ la p


[ GCD(A,B)= ] P R p


```




12. Compute the Least Common Multiple(LCM) between two userdefined numbers `A` and `B`:


14. Compute the Least Common Multiple(LCM) between two userdefined numbers `A` and `B`:


```


[ Enter A: ] P R ? d sA


[ Enter B: ] P R ? d SA


@ 207,7 +223,7 @@ LA lA * r /


[ LCM(A,B)= ] P R p


```




13. Find the roots of a quadratic equation of the form:


15. Find the roots of a quadratic equation of the form:


$$ax^2 + bx + c = 0$$




with $$a,b,c \in \mathbb{R}, a \neq 0$$


@ 219,8 +235,7 @@ $$x_{1,2} = \frac{b \pm \sqrt{b^2  4ac}}{2a}$$


#!/usr/local/bin/dc f


# GIVEN A QUADRATIC EQUATION OF THE FORM


# AX^2 + BX + C = 0


# COMPUTE ITS REAL ROOTS


# THIS PROGRAM DOES NOT WORK WITH CMPLX NUMBERS


# COMPUTE ITS REAL OR COMPLEX ROOTS


# DEVELOPED BY MARCO CETICA 2023


#


3 k


@ 236,6 +251,50 @@ lB 1 * lD + lA # POSITIVE DELTA


[ X2: ] P R LS lS p


```




16. Generate $n$ (pseudo)random numbers from userdefined range:


```


5 k


[ lA lB @ p ] sR


[ Enter number of samples: ] P ? sN


[ Enter lower bound: ] P ? sA


[ Enter upper bound: ] P ? sB


[ lR x r 1 + d lN >=L ] sL


0 lL x


```




17. Estimate $\pi$ using Monte Carlo simulation:


```


10 k


[ 0 1 @ sX 0 1 @ sY ] sR


[ lX 2 ^ sX lY 2 ^ sY ] sQ


[ 0 ;A 1 + 0 :A ] sI


[ 1 lX lY + <=I ] sC


[ lR x lQ x lC x 1 + d lN >=L ] sL


0 0 :A


0 6500 sN


lL x 0 ;A lN /


4 * p


```




18. Convert a 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


[ , ] sc


[ [ RGB( ] P 2 ;A P lc p. 1 ;A P lc p. 0 ;A P [ ) ] p. [ = ] p. lV ph ] x


```




19. Find the mean of the following temperatures(Celsius): `[25, 15, 9.5, 10, 20, 16, 20]`:


```


4 k


25 15 9.5 10 20 16 20


SX SX SX SX SX SX SX


gM p # Prints 16.5000


```




## License




[GPLv3](https://choosealicense.com/licenses/gpl3.0/)



Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
bin/dc1.0.61x86_64.pkg.tar.zst
Normal file
BIN
bin/dc1.0.61x86_64.pkg.tar.zst
Normal file
Binary file not shown.
BIN
bin/dc1.0.62.x86_64.rpm
Normal file
BIN
bin/dc1.0.62.x86_64.rpm
Normal file
Binary file not shown.
BIN
bin/dc1.0.6.x86_64.deb
Normal file
BIN
bin/dc1.0.6.x86_64.deb
Normal file
Binary file not shown.
21
main.cpp
21
main.cpp
@ 5,11 +5,11 @@


#include <iterator>


#include <fstream>




#include "src/types.h"


#include "src/adt.h"


#include "src/eval.h"


#include "src/macro.h" // for split static method




#define DC_VERSION "1.0.3"


using namespace dc;




void helper() {


std::cout << "RPN desktop calculator with macro support. Usage: \n"


@ 19,6 +19,18 @@ void helper() {


<< "V, version  Show version" << std::endl;


}




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;


std::cout << "The original version of the dc command was written by Robert Morris\n"


<< "and Lorinda Cherry. This version of dc is developed by Marco Cetica.\n" << std::endl;


std::cout << "Project homepage: <https://git.marcocetica.com/marco/dc>.\n"


<< "Email bug reports to: <email@marcocetica.com>." << std::endl;


}




int main(int argc, char **argv) {


int opt;


const char *short_opts = "e:f:hV";


@ 27,7 +39,7 @@ int main(int argc, char **argv) {


std::string stdin_expression;


bool execute_expression = false;


bool execute_file = false;


dc_stack_t stack;


Stack<std::string> stack;


std::unordered_map<char, Register> regs;


Parameters parameters = {


.precision = 0,


@ 55,7 +67,7 @@ int main(int argc, char **argv) {


}


break;


case 'V': {


std::cout << "dc v" << DC_VERSION << std::endl;


version();


return 0;


}


break;


@ 115,6 +127,7 @@ int main(int argc, char **argv) {


// Handle errors


if(err != std::nullopt) {


std::cerr << err.value() << std::endl;


return 1;


}


}





306
man.md
306
man.md
@ 3,7 +3,7 @@ title: dc


section: 1


header: General Commands Manual


footer: Marco Cetica


date: November 28, 2023


date: April 22, 2024









@ 21,8 +21,8 @@ RPN desktop calculator with macro support. Usage:




# DESCRIPTION


**dc** is an advanced, scientific and programmable RPN desktop calculator with macro support (re)written in C++.


By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions.


Its capabilities can be further extended by writing userdefined programs using the embedded, turingcomplete, macro system.


By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions and


its capabilities can be further extended by writing userdefined programs using the embedded, turingcomplete, macro system.




**dc** uses the reverse polish notation(**RPN**) to parse mathematical expressions. Unlike the infix notation, where operators


are placed _between_ operands, the polish notation(also called prefix notation) places operators _before_ the operands. The **reverse**


@ 40,12 +40,27 @@ In RPN, this would be:


```




Operands are pushed onto the stack following the LIFO policy; operators, on the other hand, pop one or more values


from the stack and push back the result. By default **dc** is very quiet, in order to inquiry the stack you need to use one of the supported


from the stack and push back the result. By default, **dc** is very quiet, in order to inquiry the stack you need to use one of the supported


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. Furthermore, you can decide to evaluate an expression


without opening the REPL by using the `e` flag.




# PROGRAMMING IN DC


As a stackbased, 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 built up towards the complete solution. Following this programming paradigm means


creating many short and simple routines that are defined either in terms of existing routines or in terms of builtin 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 be as concrete as possible.


Another advantage of this approach is the ability to test and debug interactively each definition and each routine as you build up your solution, without having


to rely on external testing framework. The dc programming language is also very extensible: the core language consists on only a few primitives, which are enough for


building new programs and extending the programming language itself. Also, dc  just like LISPlike languages, is homoiconic: there is no distinction between code and data.




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


realworld objects into templates is essential for many modern programming use cases. In fact, dc excels when used for what it was originally


developed for: computations and small mathoriented programs.




# ARCHITECTURE


As an advanced scientific calculator, **dc** has a complex architecture defined by the following two data structures:




@ 85,23 +100,25 @@ The _main stack_ is the primary form of memory available in this program. Every


within the main stack. The _main stack_ is virtually infinite and grows as much as needed; the _main stack_ is **public**, i.e. it is


shared between any **dc** command.




The **register** is an hash maplike abstract data type that allows users to operate on an _isolated_ environment formed by a _stack_


The **register** is a hash maplike abstract data type that allows users to operate on an _isolated_ environment formed by a _stack_


and an _array_. Each instance of the register is an ordered pair `(key, value)` where the _key_ is a character representing the name of the


register and the _value_ is a **private** instance of a stack and a **private** instance of an array. **dc** commands  exception made for registers, macro and array commands 


cannot operate directly on the auxiliary stacks or on the auxiliary arrays. In order to use a value stored on an auxiliary stack, you need to pop it


and push it onto the main stack(see the register section).




Both the _main stack_ and the _auxiliary stack_ implement the same abstract data type, therefore any kind of data type supported by the main stack  as well as any other property or feature supported by the main stack  is also supported by the register's stack.


Both the _main stack_ and the _auxiliary stack_ implement the same abstract data type, therefore any kind of data type supported by the main


stack  as well as any other property or feature supported by the main stack  is also supported by the register's stack.




_Arrays_ are dynamic, homogeneous and private abstract data type associated with a register.


The underlying data type of a dc array is a hashmap where the index is represented by


The underlying data type of dc array is a hashmap where the index is represented by


the map's `key` and the associated value is represented by the map's `value`.




# TYPE SYSTEM


By default each value of any kind of stack is represented by a string. Each operation is in charge to type convert the value before and after


By default, each value of any kind of stack is represented by a string. Each operation is in charge to type convert the value before and after


their invocation. The user can store both numeric and alphanumeric values on the stack. The latter using the _macro_ syntax(see below).




Arrays are homogeneous, thus the only supported data type is the `string`(the internal string type and not the **dc** one).


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.


@ 120,7 +137,7 @@ Pops off the value on top of the stack, without altering the stack.




Prints the entire contents of the stack without altering anything.




## Arithmetic


## Mathematics




**+**




@ 162,15 +179,28 @@ Pops one value, computes its square root, and pushes that.




**!**




Pops one value, computes its factorial, and pushes that.


Pops one nonnegative integer, computes its factorial, and pushes that.




**pi**




Pushes pi approximation


Pushes pi approximation.




**e**




Pushes e approximation


Pushes e approximation.




**@**




Pops two values from the stack and generate a random number, using the first value popped as the upper bound and the second popped as the lower bound.


The random value is generated using a 64bit Mersenne Twister pseudorandom number generator and a real uniform distribution.




**$**




Pops one value from the stack and convert it to the nearest integer of lesser magnitute.




**y**




Pops one value from the stack and compute the _common_(base10) logarithm of that number.




## Trigonometrical




@ 198,6 +228,120 @@ Pops one value, computes its `acos`, and pushes that.




Pops one value, computes its `atan`, and pushes that.




## Complex Numbers


**dc** allows you to operate on complex numbers in an easy and straightforward way:


most arithmetical functions that work with real numbers also work with complex numbers. Complex


numbers are stored on the stack just like any other real, rational, integer or natural number. To enter


a new complex number of the form




$$


z = a + ib


$$




write the _real_ part($a$) into the _secondtotop_ of the stack, the _imaginary_ part($b$) into the _head_ of the stack and then issue the `b` command. For example, to enter $(9 + 4i)$, write:




```


9 4 b p


(9,4)


```




As you can see, complex numbers are represented as a tuple $(Re,Im)$ and can be printed just like


any other `dc` type.




You can then operate on them just like any other number. Consider the following example:




```


(6 + 4i)  (5  2i)





(7 + i)^2


```




This can easily be calculated using the following sequence of commands:




```


10 k


6 4 b 5 2 b  7 1 b 2 ^ / p


(0.0528000000,0.1096000000)


```




And if you want to extract only the real or imaginary part of the number, issue one of the


following command, respectively:




```


re p .x im p


0.0528000000


0.1096000000


```




That is:




**re**




Pops one value, if it is a complex number retrieve its real part and pushes that.




**im**




Pops one value, if it is a complex number retrieve its imaginary part and pushes that.




## Statistics Operations


**dc** supports various common statistics operations, such as permutations, combinations, mean, standard deviation,


summation, sum of squares and linear regression. Most statistics functions are limited to **nonnegative integers**.


The accumulating functions(such as the mean, the standard deviation and the linear regression) use the `X` and


the `Y` registers.




**gP**




Pops two nonnegative integers(that is, >=0) and computes `P_{y, x}`, that is the number of possible different arrangements of


`y` different items taken in quantities of `x` items at a time. No item shall occur more than once in an arrangement and different orders of same `x`


items are counted separately. The `y` parameter correspond to the second one value popped while the `x` parameter correspond to the first one popped.




**gC**




Pops two nonnegative integers(that is, >=0) and computes `C_{y, x}`, that is the number of possible sets of


`y` different items taken in quantities of `x` items at a time. No item shall occur more than once in a set and different orders of the same `x`


items are not counted separately. The `y` parameter correspond to the second one value popped while the `x` parameter correspond to the first one popped.




**gs**




Computes Î£x of the `X` register's stack and pushes that.




**gS**




Computes Î£x^2 of the `X` register's stack and pushes that.




**gM**




Computes xÌ„(mean) of the `X` register's stack and pushes that.




**gD**




Computes Ïƒ(standard deviation) of the `X` register's stack and pushes that.




**gL**




Computes linear regression of the `X` register's stack and the `Y` register's stack. Linear regression is a


simple statistical model to find a relationship beetween a *dependent variable*(`Y`) and an independent


variable(`X`). This function will compute the following linear equation:


$$


y = mx + b


$$




using the following formulae:




```


( n * âˆ‘(x_i * y_i) )  ( âˆ‘x_i * âˆ‘y_i )


m = 


n * âˆ‘x^2  (âˆ‘x)^2






âˆ‘y_i  (m * âˆ‘x_i)


b = 


n


```




Where **n** is the number of elements of each set.


The results  the _slope_ **m** and the _yintercept_ **b**  will be pushed onto the stack in that order.




## Base Conversion




**pb**


@ 212,6 +356,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**


@ 230,6 +402,20 @@ Reverses the order of the top two values of the stack. This can also be accompli




Pops the topofstack without printing it




**.x**




Retrieve the value that was last the topofstack *before execution of any operation*(stack, macro, arithmetical, etc.).


This function can be used to quickly key back numbers when you are recovering from errors, such as typing in the wrong number


or executing the wrong operation.




**.y**




Retrieve the value that was last the secondtotop of the stack *before execution of any operation*(stack, macro, arithmetical, etc.).




**.z**




Retrieve the value that was last the thirdtotop of the stack *before execution of any operation*(stack, macro, arithmetical, etc.).




## Parameters


**dc** has three parameters that control its operation: the *precision*, the *input radix* and the *output radix*.


The precision specifies the number of fraction digits to keep in the result of most arithmetical operations. The


@ 273,20 +459,22 @@ Pushes the curent output radix on the stack


Pushes the current precision on the stack




## Register(Stack)


As mentioned before, **dc** supports an hashmap ADT called **register** represented by an ordered pair `(key, value)`.


As mentioned before, **dc** supports a hashmap ADT called **register** represented by an ordered pair `(key, value)`.


A register maps the `key`(represented by a single character) with a `value`(represented by an auxiliary stack and a private array).


At least 256 registers are available. Below, you can see the supported operations on register's stack.




**s**`r`




Pop the value off the top of the (main) stack and store it into top of the stack of register _r_.


This overwrite the top of the stack and does **NOT** follow the LIFO policy.


This overwrites the top of the stack and does **NOT** follow the LIFO policy.




**l**`r`




Copy the value in top of the stack of register _r_ and push it onto the main stack.


The value 0 is retrieved if the register is uninitialized. This does not alter the contents of _r_.




The current register value is the top of the register's stack.




**S**`r`




Pop the value off the top of the (main) stack and push it onto the stack of register _r_.


@ 297,6 +485,14 @@ The previous of the register becomes inaccessible, thus it follows the LIFO poli


Pop the value off the top of register _r_'s stack and push it onto the main stack. The previous value in register _r_'s stack, if any,


is now accessible via the **l**r command.




**c**`r`




Clear the register `r` from memory.




**z**`r`




Pushes the current register's stack depth: the number of objects on the register's stack before the execution of the **z**`r` command.




## Register(Array)


Arrays support random access through an index. You can store a value in an array and retrieve it later.




@ 310,11 +506,21 @@ the array `r`, indexed by the topofstack value.


Pops the topofstack and uses it as an index into array `r`. The selected value


is then pushed onto the stack.




## Strings


## Strings/Macros




_dc_ has a limited ability to operate on strings as well as on numbers; the only things you can do with strings are print them and execute them as macros (which means that the content of a string can executed as a _dc_ program). Any kind of stack can hold strings, and _dc_ always knows whether any given object is a string or a number.


Some commands such as arithmetic operations demand numbers as arguments and print errors if given strings.


Other commands can accept either a number or a string; for example, the **p** command can accept either and prints the object according to its type.


_dc_ has a limited ability to operate on strings as well as on numbers; strings can be printed or executed as a macro, that is as a dc program.


Strings can be nested, i.e. a string can contain another string. For example:




 **\[ 1 \] p**: Prints the string(note: `1` is a string and not a number);


 **\[ \[ Hello World \] p \] x**: pushes a string containing another string and a command and execute it;


 **\[ Executing: \] p \[ 2 p 10 p \[ r / p \] p x \] x**: Prints the ongoing operation and execute it using homoiconicity property.




When a string is used as a macro, dc *lazily evaluate* it; that is, the evaluation of subprograms is delayed until the evaluator requires their values.


Avoiding eagerly evaluation allows the programmer to take advantage of DC's homoiconicity and to make macro evaluation more lightweight.




Any kind of stack can hold strings, and _dc_ always knows whether any given object is a string or a number. Some commands such as arithmetic operations demand


numbers as arguments and print errors if given strings. Other commands can accept either a number or a string; for example, the **p**


command can accept either and prints the object according to its type.




**[ characters ]**




@ 370,6 +576,10 @@ Exit with return code `0`.




Reads a line from the terminal and executes it. This command allows a macro to request input from the user.




**'**




Pops a string off the stack, use it as a filepath, read its content and execute it.




# EXAMPLES


Below, there are some practical problems solved using **dc**.




@ 454,10 +664,64 @@ lB 1 * lD + lA # POSITIVE DELTA


[ Result in base 2: ] P R pb


```




11. Load an external file:


```


$> echo "[ p 1 + d lN >=L ] sL" > loop.dc


$> cat prg.dc


[ loop.dc ] ' # Load loop macro


[ Enter limit: ] P # Ask user for limit 'N'


? 1 + sN # Read from stdin


c 1 lL x # Clear the stack, add lower bound, load and execute macro


```




12. Generate *n* (pseudo)random numbers from userdefined range:


```


5 k


[ lA lB @ p ] sR


[ Enter number of samples: ] P ? sN


[ Enter lower bound: ] P ? sA


[ Enter upper bound: ] P ? sB


[ lR x r 1 + d lN >=L ] sL


0 lL x


```




13. Estimate *pi* using Monte Carlo simulation


```


10 k


[ 0 1 @ sX 0 1 @ sY ] sR


[ lX 2 ^ sX lY 2 ^ sY ] sQ


[ 0 ;A 1 + 0 :A ] sI


[ 1 lX lY + <=I ] sC


[ lR x lQ x lC x 1 + d lN >=L ] sL


0 0 :A


0 6500 sN


lL x 0 ;A lN /


4 * p


```




14. Convert a 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


[ , ] sc


[ [ RGB( ] P 2 ;A P lc p. 1 ;A P lc p. 0 ;A P [ ) ] p. [ = ] p. lV ph ] x


```




15. Find the mean of the following temperatures(Celsius): `[25, 15, 9.5, 10, 20, 16, 20]`:


```


4 k


25 15 9.5 10 20 16 20


SX SX SX SX SX SX SX


gM p # Prints 16.5000


```




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




# BUGS




If you encounter any kind of problem, email me at [email@marcocetica.com](mailto:email@marcocetica.com) or open an issue at [https://github.com/icebit/dc](https://github.com/icebit/dc).


If you encounter any kind of problem, email me at [email@marcocetica.com](mailto:email@marcocetica.com) or open an issue at [https://github.com/ceticamarco/dc](https://github.com/ceticamarco/dc).



@ 1,17 +1,24 @@


project(src)




set(HEADER_FILES


eval.h


macro.h


math.h


operation.h


stack.h


types.h)


eval.h


macro.h


mathematics.h


statistics.h


bitwise.h


operation.h


stack.h


adt.h


)




set(SOURCE_FILES


eval.cpp


macro.cpp


math.cpp


stack.cpp)


eval.cpp


macro.cpp


mathematics.cpp


statistics.cpp


bitwise.cpp


stack.cpp


adt.cpp


)




add_library(src STATIC ${SOURCE_FILES} ${HEADER_FILES})



180
src/adt.cpp
Normal file
180
src/adt.cpp
Normal file
@ 0,0 +1,180 @@


#include <numeric>


#include "adt.h"




#define GET_X this>stack.back()


#define GET_Y this>stack.at(this>stack.size()  2)


#define GET_Z this>stack.at(this>stack.size()  3)




namespace dc {


/**


* Add @value to the stack


*/


template<typename T>


requires is_num_or_str<T>


void Stack<T>::push(T value) {


this>stack.push_back(value);


}




/**


* Clear the stack


*/


template<typename T>


requires is_num_or_str<T>


void Stack<T>::clear() {


if(!this>stack.empty()) {


this>stack.clear();


}


}




/**


* If @remove is true, pop head of the stack


* otherwise return it without popping.


* If stack is empty it causes undefined behavior


*/


template<typename T>


requires is_num_or_str<T>


T Stack<T>::pop(bool remove) {


T value = this>stack.back();




if(remove) {


this>stack.pop_back();


}




return value;


}




/**


* Make a copy of head, 2nd and 3rd element


* of the stack


*/


template<typename T>


requires is_num_or_str<T>


void Stack<T>::copy_xyz() {


if(this>stack.size() >= 1) {


this>last_x = GET_X;


}




if(this>stack.size() >= 2) {


this>last_y = GET_Y;


}




if(this>stack.size() >= 3) {


this>last_z = GET_Z;


}


}




/**


* Returns last x of the stack


* If stack is empty returns '0' or empty string


*/


template<typename T>


requires is_num_or_str<T>


T Stack<T>::get_last_x() {


return this>last_x;


}




/**


* Returns last y of the stack


* If stack is empty returns '0' or empty string


*/


template<typename T>


requires is_num_or_str<T>


T Stack<T>::get_last_y() {


return this>last_y;


}




/**


* Returns last z of the stack


* If stack is empty returns '0' or empty string


*/


template<typename T>


requires is_num_or_str<T>


T Stack<T>::get_last_z() {


return this>last_z;


}




/**


* Reads stack at @index


*/


template<typename T>


requires is_num_or_str<T>


T& Stack<T>::at(std::size_t index) {


T &value = this>stack.at(index);




return value;


}




/**


* Reads stack at @index using '[]' syntax


*/


template<typename T>


requires is_num_or_str<T>


T& Stack<T>::operator[](std::size_t index) {


return at(index);


}




/**


* Returns stack size


*/


template<typename T>


requires is_num_or_str<T>


std::size_t Stack<T>::size() {


return this>stack.size();


}




/*


* Returns the summation of all items


*/


template<typename T>


requires is_num_or_str<T>


double Stack<T>::summation() {


auto sum = std::accumulate(this>stack.begin(), this>stack.end(), 0.0,


[](auto accumulator, const T& val) > double {


if constexpr(std::is_same_v<T, std::string>) {


return accumulator + std::stod(val);


} else {


return accumulator + val;


}


});




return sum;


}




/*


* Returns the summation of squares of all items


*/


template<typename T>


requires is_num_or_str<T>


double Stack<T>::summation_squared() {


auto sum = std::accumulate(this>stack.begin(), this>stack.end(), 0.0,


[](auto accumulator, const T& val) > double {


if constexpr(std::is_same_v<T, std::string>) {


return accumulator + (std::stod(val) * std::stod(val));


} else {


return accumulator + (val * val);


}


});




return sum;


}






/**


* Returns true if stack is empty


* false otherwise


*/


template<typename T>


requires is_num_or_str<T>


bool Stack<T>::empty() {


return this>stack.empty();


}




/**


* Returns a const reference to the stack


*/


template<typename T>


requires is_num_or_str<T>


const std::vector<T>& Stack<T>::get_ref() const {


return this>stack;


}


}

48
src/adt.h
Normal file
48
src/adt.h
Normal file
@ 0,0 +1,48 @@


#pragma once


#include <vector>


#include <string>


#include <cstdint>


#include <unordered_map>




namespace dc {


template<typename T>


concept is_num_or_str = (std::is_arithmetic_v<T>  std::is_same_v<T, std::string>);




template<typename T>


requires is_num_or_str<T>


class Stack {


public:


void push(T value);


void clear();


void copy_xyz();


T pop(bool remove);


T get_last_x();


T get_last_y();


T get_last_z();


T& at(std::size_t index);


T& operator[](std::size_t index);


std::size_t size();


double summation();


double summation_squared();


bool empty();


[[nodiscard]] const std::vector<T>& get_ref() const;




private:


std::vector<T> stack;


T last_x{};


T last_y{};


T last_z{};


};




typedef struct {


Stack<std::string> stack;


std::unordered_map<int, std::string> array;


} Register;




enum class radix_base : std::uint8_t { BIN = 2, OCT = 8, DEC = 10, HEX = 16 };


typedef struct {


unsigned int precision;


unsigned short iradix;


radix_base oradix;


} Parameters;


}

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[len1];


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[len1];


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[len1];


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[len1];


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];

