Fixed various bugs and fixed number representation error

This commit is contained in:
Marco Cetica 2023-11-03 10:48:30 +01:00
parent 09985c928b
commit 195fb4476a
Signed by: marco
GPG Key ID: 45060A949E90D0FD
9 changed files with 70 additions and 649 deletions

4
.gitignore vendored
View File

@ -30,3 +30,7 @@
*.exe
*.out
*.app
dc
# Manual
dc.1

View File

@ -58,6 +58,11 @@ $> make clean all
A new binary called `dc` will be created in your local folder.
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
```
## Usage
dc can be used in three different ways:
1. From the interactive REPL(run it without any argument);

BIN
dc

Binary file not shown.

523
dc.1
View File

@ -1,523 +0,0 @@
.\" Automatically generated by Pandoc 2.17.1.1
.\"
.\" Define V font for inline verbatim, using C font in formats
.\" that render this, and otherwise B font.
.ie "\f[CB]x\f[]"x" \{\
. ftr V B
. ftr VI BI
. ftr VB B
. ftr VBI BI
.\}
.el \{\
. ftr V CR
. ftr VI CI
. ftr VB CB
. ftr VBI CBI
.\}
.TH "dc" "1" "November 02, 2023" "Marco Cetica" "General Commands Manual"
.hy
.SH NAME
.PP
dc \[mi] RPN desktop calculator with macro support
.SH SYNOPSIS
.IP
.nf
\f[C]
RPN desktop calculator with macro support. Usage:
-e, --expression <EXPRESSION> | Evaluate an expression
-f, --file <FILE> | Evaluate a file
-h, --help | Show this helper
-V, --version | Show version
\f[R]
.fi
.SH DESCRIPTION
.PP
\f[B]dc\f[R] 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 numerical functions.
Its capabilities can be further extended by writing user-defined
programs using the embedded, turing-complete, macro system.
.PP
\f[B]dc\f[R] uses the reverse polish notation(\f[B]RPN\f[R]) to parse
mathematical expressions.
Unlike the infix notation, where operators are placed \f[I]between\f[R]
operands, the polish notation(also called prefix notation) places
operators \f[I]before\f[R] the operands.
The \f[B]reverse\f[R] polish notation takes this concept even further by
placing the operators \f[I]after\f[R] the operands.
As an example, consider the following infix expression:
.IP
.nf
\f[C]
(((5 + 4) * (3 - 2)) / 2)
\f[R]
.fi
.PP
In RPN, this would be:
.IP
.nf
\f[C]
2 5 4 + 3 2 - * r / p
\f[R]
.fi
.PP
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 \f[B]dc\f[R] is very quiet, in order to inquiry the stack you
need to use one of the supported options(see below).
.PP
\f[B]dc\f[R] reads from the standard input, but it can also work with
text files using the \f[V]-f\f[R] flag.
Futhermore, you can decide to evaluate an expression without opening the
REPL by using the \f[V]-e\f[R] flag.
.SH ARCHITECTURE
.PP
As an advanced scientific calculator, \f[B]dc\f[R] has a complex
architecture defined by the following two data structures:
.IP "1." 3
The main stack;
.PD 0
.P
.PD
.IP "2." 3
The register.
.PP
The \f[I]register\f[R] can also be extended as follows:
.IP "1." 3
The register stack;
.PD 0
.P
.PD
.IP "2." 3
The register array.
.PP
The \f[I]main stack\f[R] is the primary form of memory of this program.
Every time you enter a number or execute a command, you are operating
within the main stack.
The \f[I]main stack\f[R] is virtually infinite and grows as much as
needed; the \f[I]main stack\f[R] is \f[B]public\f[R], i.e.\ it is shared
between any \f[B]dc\f[R] command.
.PP
The \f[B]register\f[R] is an hash map-like abstract data type that
allows users to operate on an \f[I]isolated\f[R] environment formed by a
\f[I]stack\f[R] and an \f[I]array\f[R].
Each instance of the register is an ordered pair \f[V](key, value)\f[R]
where the \f[I]key\f[R] is a character representing the name of the
register and the \f[I]value\f[R] is a \f[B]private\f[R] instance of a
stack and a \f[B]private\f[R] instance of an array.
\f[B]dc\f[R] commands - exception made for registers, macro and array
commands - cannot operate directly on the auxiliary stack or on the
auxiliary array.
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).
.PP
Both the \f[I]main stack\f[R] and the \f[I]auxiliary stack\f[R]
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\[cq]s
stack.
.PP
\f[I]Arrays\f[R] 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 map\[cq]s \f[V]key\f[R] and the associated value is
represented by the map\[cq]s \f[V]value\f[R].
.SH TYPE SYSTEM
.PP
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 numerical and alphanumerical values on the
stack.
The latter using the \f[I]macro\f[R] syntax(see below).
.PP
Arrays are homogeneous, thus the only supported data type is the
\f[V]string\f[R](the internal string type and not the \f[B]dc\f[R] one).
.SH COMMANDS
.PP
Below, there is a list of supported \f[B]dc\f[R] commands.
.SS Printing Commands
.PP
\f[B]p\f[R]
.PP
Prints the value on the top of the stack, without altering the stack.
A newline is printed after the value.
.PP
\f[B]P\f[R]
.PP
Pops off the value on top of the stack, without altering the stack.
.PP
\f[B]f\f[R]
.PP
Prints the entire contents of the stack without altering anything.
.SS Arithmetic
.PP
\f[B]+\f[R]
.PP
Pops two values off the stack, adds them, and pushes the result.
.PP
\f[B]-\f[R]
.PP
Pops two values, subtracts the first one popped from the second one
popped, and pushes the result.
.PP
\f[B]*\f[R]
.PP
Pops two values, multiplies them, and pushes the result.
.PP
\f[B]/\f[R]
.PP
Pops two values, divides the second one popped from the first one
popped, and pushes the result.
.PP
\f[B]%\f[R]
.PP
Pops two values, computes the remainder of the division between the
second one popped and the first one popped.
Pushes back the result.
.PP
\f[B]\[ti]\f[R]
.PP
Pops two values, divides the second one popped from the first one
popped.
The quotient is pushed first, and the remainder is pushed next.
.PP
\f[B]\[ha]\f[R]
.PP
Pops two values and computes their exponentiated, using the first value
popped as the exponent and the second popped as the base.
.PP
\f[B]|\f[R]
.PP
Pops three values and computes a modular exponentiation.
The first value popped is used as the reduction modulus; this value must
be a non-zero integer.
The second popped is used as the exponent; this value must be a
non-negative number.
The third value popped is the base which gets exponentiated, which
should also be an integer.
This function computes the following modular equivalence:
\f[V]c \[==] b\[ha]e (mod n)\f[R]
.PP
\f[B]v\f[R]
.PP
Pops one value, computes its square root, and pushes that.
.PP
\f[B]!\f[R]
.PP
Pops one value, computes its factorial, and pushes that.
.PP
\f[B]pi\f[R]
.PP
Pushes pi approximation
.PP
\f[B]e\f[R]
.PP
Pushes e approximation
.SS Trigonometrical
.PP
\f[B]sin\f[R]
.PP
Pops one value, computes its \f[V]sin\f[R], and pushes that.
.PP
\f[B]cos\f[R]
.PP
Pops one value, computes its \f[V]cos\f[R], and pushes that.
.PP
\f[B]tan\f[R]
.PP
Pops one value, computes its \f[V]tan\f[R], and pushes that.
.SS Base Conversion
.PP
\f[B]pb\f[R]
.PP
Prints the value on top of the stack in base 2, without altering the
stack.
A newline is printed after the value.
.PP
\f[B]po\f[R]
.PP
Prints the value on top of the stack in base 8, without altering the
stack.
A newline is printed after the value.
.PP
\f[B]px\f[R]
.PP
Prints the value on top of the stack in base 16, without altering the
stack.
A newline is printed after the value.
.SS Stack Control
.PP
\f[B]c\f[R]
.PP
Clears the stack, rendering it empty.
.PP
\f[B]d\f[R]
.PP
Duplicates the value on the top of the stack, pushing another copy of
it.
Thus, \f[V]4 d * p\f[R] computes 4 squared and prints it.
.PP
\f[B]r\f[R]
.PP
Reverses the order of the top two values of the stack.
This can also be accomplished with the sequence \f[V]Sa Sb La Lb\f[R].
.PP
\f[B]R\f[R]
.PP
Pops the top-of-stack without printing it
.SS Register(Stack)
.PP
As mentioned before, \f[B]dc\f[R] supports an hashmap ADT called
\f[B]register\f[R] represented by an ordered pair
\f[V](key, value)\f[R].
A register maps the \f[V]key\f[R](represented by a single character)
with a \f[V]value\f[R](represented by an auxiliary stack and a private
array).
At least 256 registers are available.
Below, you can see the supported operations on register\[cq]s stack.
.PP
\f[B]s\f[R]\f[V]r\f[R]
.PP
Pop the value off the top of the (main) stack and store it into top of
the stack of register \f[I]r\f[R].
This overwrite the top of the stack and does \f[B]NOT\f[R] follow the
LIFO policy.
.PP
\f[B]l\f[R]\f[V]r\f[R]
.PP
Copy the value in top of the stack of register \f[I]r\f[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 \f[I]r\f[R].
.PP
\f[B]S\f[R]\f[V]r\f[R]
.PP
Pop the value off the top of the (main) stack and push it onto the stack
of register \f[I]r\f[R].
The previous of the register becomes inaccessible, thus it follows the
LIFO policy.
.PP
\f[B]L\f[R]\f[V]r\f[R]
.PP
Pop the value off the top of register \f[I]r\f[R]\[cq]s stack and push
it onto the main stack.
The previous value in register \f[I]r\f[R]\[cq]s stack, if any, is now
accessible via the \f[B]b\f[R]r command.
.SS Register(Array)
.PP
Arrays support random access through an index.
You can store a value in an array and retrieve it later.
.PP
\f[B]:\f[R]\f[V]r\f[R]
.PP
Will pop the top two values off the stack.
The second-to-top value will be stored in the array \f[V]r\f[R], indexed
by the top-of-stack value.
.PP
\f[B];\f[R]\f[V]r\f[R]
.PP
Pops the top-of-stack and uses it as an index into array \f[V]r\f[R].
The selected value is then pushed onto the stack.
.SS Strings
.PP
\f[I]dc\f[R] 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 \f[I]dc\f[R] program).
Any kind of stack can hold strings, and \f[I]dc\f[R] 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
\f[B]p\f[R] command can accept either and prints the object according to
its type.
.PP
\f[B][ characters ]\f[R]
.PP
Makes a string containing \f[I]characters\f[R] (contained between
balanced \f[B][\f[R] and \f[B]]\f[R] characters), and pushes it on the
stack.
For example, \f[B][ Hello World ] P\f[R] prints the string \f[B]Hello
World\f[R] (with no newline).
.PP
\f[B]x\f[R]
.PP
Pops a value off the stack and executes it as a macro.
Normally it should be a string; if it is a number, it is simply pushed
back onto the stack.
For example, \f[B][ 1 p ] x\f[R] executes the macro \f[B]1 p\f[R] which
pushes \f[B]1\f[R] on the stack and prints \f[B]1\f[R] on a separate
line.
.PP
Macros are most often stored in register\[cq]s stacks; \f[B][ 1 p ]
sa\f[R] stores a macro to print \f[B]1\f[R] into register\[cq]s stack
\f[B]a\f[R], and \f[B]la x\f[R] invokes this macro.
.PP
\f[B]>\f[R]\f[V]r\f[R]
.PP
Pops two values off the stack and compares them assuming they are
numbers, executing the contents of register \f[I]r\f[R] as a macro if
the original top-of-stack is greater.
Thus, \f[B]1 2>a\f[R] will invoke register \f[B]a\f[R]\[cq]s contents
and \f[B]2 1>a\f[R] will not.
.PP
\f[B]=>\f[R]\f[V]r\f[R]
.PP
Similar but invokes the macro if the original top-of-stack is greater or
equal to the second-to-top.
.PP
\f[B]<\f[R]\f[V]r\f[R]
.PP
Similar but invokes the macro if the original top-of-stack is less.
.PP
\f[B]<=\f[R]\f[V]r\f[R]
.PP
Similar but invokes the macro if the original top-of-stack is less or
equal to the second-to-top.
.PP
\f[B]=\f[R]\f[V]r\f[R]
.PP
Similar but invokes the macro if the two numbers popped are equal.
.PP
\f[B]!=\f[R]\f[V]r\f[R]
.PP
Similar but invokes the macro if the two numbers popped are not equal.
.SS Status Inquiry
.PP
\f[B]Z\f[R]
.PP
Pops a value off the stack, calculates the number of digits it has (or
number of characters, if it is a string) and pushes that number.
.PP
\f[B]z\f[R]
.PP
Pushes the current stack depth: the number of objects on the stack
before the execution of the \f[B]z\f[R] command.
.SS Miscellaneous
.PP
\f[B]q\f[R]
.PP
Exit with return code \f[V]0\f[R].
.PP
\f[B]?\f[R]
.PP
Reads a line from the terminal and executes it.
This command allows a macro to request input from the user.
.SH EXAMPLES
.PP
Below, there are some practical problems solved using \f[B]dc\f[R].
.IP "1." 3
Evaluate \f[V](-5 + sqrt(25 - 16)) / 2\f[R]:
.IP
.nf
\f[C]
-5 25 16 - v + 2 / p
\f[R]
.fi
.IP "2." 3
Evaluate \f[V]sin(2pi)+cos(2pi)\f[R]:
.IP
.nf
\f[C]
2 pi * sin 2 pi * cos + p
\f[R]
.fi
.IP "3." 3
Loop from 1 to \f[V]n\f[R], where \f[V]n\f[R] is a user-defined value:
.IP
.nf
\f[C]
[ p 1 + d lN >L ] sL # Print numbers from 1 through \[aq]N\[aq]
[ Enter limit: ] P # Ask user for limit \[aq]N\[aq]
? 1 + sN # Read from stdin
c 1 lL x # Clear the stack, add lower bound, load and execute macro
\f[R]
.fi
.IP "4." 3
Sum the first \f[V]n\f[R] natural numbers, where \f[V]n\f[R] is a
user-defined value:
.IP
.nf
\f[C]
[ Enter bound: ] P ?
[ d 1 - d 1 <F + ] d sF x p
\f[R]
.fi
.IP "5." 3
Prints the first 20 values of \f[V]n!\f[R]:
.IP
.nf
\f[C]
[ la 1 + d sa * p la 20 >y ] sy
0 sa 1
ly x
\f[R]
.fi
.IP "6." 3
Computes the factorial of a given number:
.IP
.nf
\f[C]
[ ln 1 - sn ln la * sa ln 1 !=f ] sf
[ Enter value: ] P ? sn
ln sa
lf x
la p
\f[R]
.fi
.IP "7." 3
Computes the Greatest Common Divisor(GCD) between two user-defined
numbers \f[V]A\f[R] and \f[V]B\f[R]:
.IP
.nf
\f[C]
[ Enter A: ] P R ?
[ Enter B: ] P R ?
[ d Sa r La % d 0 <a ] d sa x +
[ GCD(A,B)= ] P R p
\f[R]
.fi
.IP "8." 3
Computes the Least Common Multiple(LCM) between two user-defined numbers
\f[V]A\f[R] and \f[V]B\f[R]:
.IP
.nf
\f[C]
[ Enter A: ] P R ? d sA
[ Enter B: ] P R ? d SA
[ d Sa r La % d 0 <a ] d sa x +
LA lA * r /
[ LCM(A,B)= ] P R p
\f[R]
.fi
.IP "9." 3
Find the roots of a quadratic equation
.IP
.nf
\f[C]
[ Enter A: ] P ? sA
[ Enter B: ] P ? sB
[ Enter C: ] P ? sC
lB 2 \[ha] 4 lA lC * * - v sD
lB -1 * lD - lA # NEGATIVE DELTA
2 * / sS # FIRST SOLUTION
lB -1 * lD + lA # POSITIVE DELTA
2 * / SS # SECOND SOLUTION
[ X: ] P R lS p
[ Y: ] P R LS lS p
\f[R]
.fi
.SH AUTHORS
.PP
The original version of the \f[B]dc\f[R] command was written by Robert
Morris and Lorinda Cherry.
This version of \f[B]dc\f[R] is developed by Marco Cetica.
.SH BUGS
.PP
If you encounter any kind of problem, email me at
<email@marcocetica.com> or open an issue at
<https://github.com/ice-bit/dc>.

View File

@ -68,6 +68,7 @@ int main(int argc, char **argv) {
// Handle errors
if(err != std::nullopt) {
std::cerr << err.value() << std::endl;
return 1;
}
return 0;

14
man.md
View File

@ -163,20 +163,6 @@ Pops one value, computes its `cos`, and pushes that.
Pops one value, computes its `tan`, and pushes that.
## Base Conversion
**pb**
Prints the value on top of the stack in base 2, without altering the stack. A newline is printed after the value.
**po**
Prints the value on top of the stack in base 8, without altering the stack. A newline is printed after the value.
**px**
Prints the value on top of the stack in base 16, without altering the stack. A newline is printed after the value.
## Stack Control
**c**

View File

@ -1,3 +1,5 @@
#include <memory>
#include "eval.h"
#include "math.h"
#include "stack.h"
@ -13,217 +15,165 @@ std::optional<std::string> Evaluate::eval() {
// NUMERICAL OPERATIONS
//
if(val == "+") {
IOperation *math = new Math(OPType::ADD);
auto math = std::make_unique<Math>(OPType::ADD);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "-") {
IOperation *math = new Math(OPType::SUB);
auto math = std::make_unique<Math>(OPType::SUB);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "*") {
IOperation *math = new Math(OPType::MUL);
auto math = std::make_unique<Math>(OPType::MUL);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val =="/") {
IOperation *math = new Math(OPType::DIV);
auto math = std::make_unique<Math>(OPType::DIV);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "%") {
IOperation *math = new Math(OPType::MOD);
auto math = std::make_unique<Math>(OPType::MOD);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "~") {
IOperation *math = new Math(OPType::DIV_MOD);
auto math = std::make_unique<Math>(OPType::DIV_MOD);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "|") {
IOperation *math = new Math(OPType::MOD_EXP);
auto math = std::make_unique<Math>(OPType::MOD_EXP);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "^") {
IOperation *math = new Math(OPType::EXP);
auto math = std::make_unique<Math>(OPType::EXP);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "v") {
IOperation *math = new Math(OPType::SQRT);
auto math = std::make_unique<Math>(OPType::SQRT);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "sin") {
IOperation *math = new Math(OPType::SIN);
auto math = std::make_unique<Math>(OPType::SIN);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "cos") {
IOperation *math = new Math(OPType::COS);
auto math = std::make_unique<Math>(OPType::COS);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "tan") {
IOperation *math = new Math(OPType::TAN);
auto math = std::make_unique<Math>(OPType::TAN);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "!") {
IOperation *math = new Math(OPType::FACT);
auto math = std::make_unique<Math>(OPType::FACT);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "pi") {
IOperation *math = new Math(OPType::PI);
auto math = std::make_unique<Math>(OPType::PI);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
} else if(val == "e") {
IOperation *math = new Math(OPType::E);
auto math = std::make_unique<Math>(OPType::E);
err = math->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete math;
}
//
// STACK OPERATIONS
//
else if(val == "p") { // PRINT TOP ELEMENT OF STACK
IOperation *stack = new Stack(OPType::PCG);
auto stack = std::make_unique<Stack>(OPType::PCG);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "P") { // PRINT TOP ELEMENT WITHOUT NEWLINE
IOperation *stack = new Stack(OPType::P);
auto stack = std::make_unique<Stack>(OPType::P);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "c") { // CLEAR THE STACK
IOperation *stack = new Stack(OPType::CLR);
auto stack = std::make_unique<Stack>(OPType::CLR);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "R") { // POP HEAD OF THE STACK WITHOUT PRINTING IT
IOperation *stack = new Stack(OPType::PH);
auto stack = std::make_unique<Stack>(OPType::PH);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "r") { // SWAP ORDER OF THE TWO TOP ELEMENTS
IOperation *stack = new Stack(OPType::SO);
auto stack = std::make_unique<Stack>(OPType::SO);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "d") { // DUPLICATE THE HEAD OF THE STACK
IOperation *stack = new Stack(OPType::DP);
auto stack = std::make_unique<Stack>(OPType::DP);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "f") { // PRINT THE WHOLE STACK
IOperation *stack = new Stack(OPType::PS);
auto stack = std::make_unique<Stack>(OPType::PS);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "Z") { // COMPUTE HEAD SIZE(NUM. OF CHARS/DIGITS)
IOperation *stack = new Stack(OPType::CH);
auto stack = std::make_unique<Stack>(OPType::CH);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "z") { // COMPUTE STACK SIZE
IOperation *stack = new Stack(OPType::CS);
auto stack = std::make_unique<Stack>(OPType::CS);
err = stack->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete stack;
} else if(val == "x") { // EXECUTE MACRO
IOperation *macro = new Macro(OPType::EX, this->regs);
auto macro = std::make_unique<Macro>(OPType::EX, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
} else if(val == "?") { // READ LINE FROM STDIN
IOperation *macro = new Macro(OPType::RI, this->regs);
auto macro = std::make_unique<Macro>(OPType::RI, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
} else if(val == "q") { // QUIT GRACEFULLY
std::exit(0);
} else {
@ -328,53 +278,41 @@ std::optional<std::string> Evaluate::parse_macro_command(std::string val) {
// execute register's content as a macro
std::optional<std::string> err = std::nullopt;
if(operation == ">") {
IOperation *macro = new Macro(OPType::CMP, Operator::GT, dc_register, this->regs);
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::GT, dc_register, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
} else if(operation == "<") {
IOperation *macro = new Macro(OPType::CMP, Operator::LT, dc_register, this->regs);
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::LT, dc_register, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
} else if(operation == "=") {
IOperation *macro = new Macro(OPType::CMP, Operator::EQ, dc_register, this->regs);
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::EQ, dc_register, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
} else if(operation == ">=") {
IOperation *macro = new Macro(OPType::CMP, Operator::GEQ, dc_register, this->regs);
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::GEQ, dc_register, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
} else if(operation == "<=") {
IOperation *macro = new Macro(OPType::CMP, Operator::LEQ, dc_register, this->regs);
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::LEQ, dc_register, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
} else if(operation == "!=") {
IOperation *macro = new Macro(OPType::CMP, Operator::NEQ, dc_register, this->regs);
auto macro = std::make_unique<Macro>(OPType::CMP, Operator::NEQ, dc_register, this->regs);
err = macro->exec(this->stack);
if(err != std::nullopt) {
return err;
}
delete macro;
}
return err;

View File

@ -50,7 +50,7 @@ std::optional<std::string> Math::fn_add(stack_t &stack) {
stack.pop_back();
// Push back the result as a string
stack.push_back(std::to_string(lhs + rhs));
stack.push_back(trim_zeros(lhs + rhs));
} else {
return "'+' requires numeric values";
}
@ -80,7 +80,7 @@ std::optional<std::string> Math::fn_sub(stack_t &stack) {
stack.pop_back();
// Push back the result as a string
stack.push_back(std::to_string(-(lhs - rhs)));
stack.push_back(trim_zeros(-(lhs - rhs)));
} else {
return "'-' requires numeric values";
}
@ -110,7 +110,7 @@ std::optional<std::string> Math::fn_mul(stack_t &stack) {
stack.pop_back();
// Push back the result as a string
stack.push_back(std::to_string(lhs * rhs));
stack.push_back(trim_zeros(lhs * rhs));
} else {
return "'*' requires numeric values";
}
@ -145,7 +145,7 @@ std::optional<std::string> Math::fn_div(stack_t &stack) {
}
// Push back the result as a string
stack.push_back(std::to_string(dividend / divisor));
stack.push_back(trim_zeros(dividend / divisor));
} else {
return "'/' requires numeric values";
}
@ -180,7 +180,7 @@ std::optional<std::string> Math::fn_mod(stack_t &stack) {
}
// Push back the result as a string
stack.push_back(std::to_string((int)lhs % (int)rhs));
stack.push_back(trim_zeros((int)lhs % (int)rhs));
} else {
return "'%' requires numeric values";
}
@ -214,8 +214,8 @@ std::optional<std::string> Math::fn_div_mod(stack_t &stack) {
auto quotient = std::trunc(dividend / divisor);
auto remainder = ((int)dividend % (int)divisor);
stack.push_back(std::to_string(quotient));
stack.push_back(std::to_string(remainder));
stack.push_back(trim_zeros(quotient));
stack.push_back(trim_zeros(remainder));
}
} else {
@ -270,7 +270,7 @@ std::optional<std::string> Math::fn_mod_exp(stack_t &stack) {
c = (c * base) % modulus;
}
stack.push_back(std::to_string(c));
stack.push_back(trim_zeros(c));
} else {
return "'|' requires numeric values";
}
@ -300,7 +300,7 @@ std::optional<std::string> Math::fn_exp(stack_t &stack) {
stack.pop_back();
// Push back the result as a string
stack.push_back(std::to_string(pow(base, exp)));
stack.push_back(trim_zeros(pow(base, exp)));
} else {
return "'^' requires numeric values";
}
@ -329,7 +329,7 @@ std::optional<std::string> Math::fn_sqrt(stack_t &stack) {
}
// Push back the result as a string
stack.push_back(std::to_string(sqrt(x)));
stack.push_back(trim_zeros(sqrt(x)));
} else {
return "'v' requires numeric values";
}
@ -354,7 +354,7 @@ std::optional<std::string> Math::fn_sin(stack_t &stack) {
stack.pop_back();
// Push back the result as a string
stack.push_back(std::to_string(sin(x)));
stack.push_back(trim_zeros(sin(x)));
} else {
return "'sin' requires numeric values";
}
@ -379,7 +379,7 @@ std::optional<std::string> Math::fn_cos(stack_t &stack) {
stack.pop_back();
// Push back the result as a string
stack.push_back(std::to_string(cos(x)));
stack.push_back(trim_zeros(cos(x)));
} else {
return "'cos' requires numeric values";
}
@ -404,7 +404,7 @@ std::optional<std::string> Math::fn_tan(stack_t &stack) {
stack.pop_back();
// Push back the result as a string
stack.push_back(std::to_string(tan(x)));
stack.push_back(trim_zeros(tan(x)));
} else {
return "'tan' requires numeric values";
}
@ -438,7 +438,7 @@ std::optional<std::string> Math::fn_fact(stack_t &stack) {
}
// Push back the result as a string
stack.push_back(std::to_string(factorial));
stack.push_back(trim_zeros(factorial));
} else {
return "'!' requires numeric values";
}
@ -447,13 +447,22 @@ std::optional<std::string> Math::fn_fact(stack_t &stack) {
}
std::optional<std::string> Math::fn_pi(stack_t &stack) {
stack.push_back(std::to_string(std::numbers::pi));
stack.push_back(trim_zeros(std::numbers::pi));
return std::nullopt;
}
std::optional<std::string> Math::fn_e(stack_t &stack) {
stack.push_back(std::to_string(std::numbers::e));
stack.push_back(trim_zeros(std::numbers::e));
return std::nullopt;
}
std::string Math::trim_zeros(double number) {
std::string s = std::to_string(number);
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
s.erase(s.find_last_not_of('.') + 1, std::string::npos);
return s;
}

View File

@ -23,6 +23,7 @@ private:
std::optional<std::string> fn_fact(stack_t &stack);
std::optional<std::string> fn_pi(stack_t &stack);
std::optional<std::string> fn_e(stack_t &stack);
static std::string trim_zeros(double number);
OPType op_type;
};