I'm trying to do the following exercise from Programming: Principles and Practices Using C++ 2Ed.
Ch6 Ex3
Add a factorial operator: use a suffix '!' operator to represent "factorial". For example, the expression 7! means 7*6*5*4*3*2*1. Make ! bind tighter than * and /; that is 7*8! means 7*(8!) rather than (7*8)!. Begin by modifying the grammar to account for a higher-level operator. To agree with standard mathematical definition of factorial, let 0! evaluate to 1. Hint: The calculator functions deal with doubles, but factorial is defined only for ints, so just for x!, assign x to an int and calculate the factorial of that int.
Prior to this question I had to clean up some errors and add the use of { and } along with ( and ), so the following code reflects those changes.
This is done within Visual Studio 2017. Grammar outline is after code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
// Chapter 6
//
#include "stdafx.h"
#include "std_lib_facilities.h"
class token // Creating a new class called token
{ // The token is a character/value pair
public:
char kind;
double value;
token(char ch)
:kind(ch), value(0) {}
token(char ch, double val)
:kind(ch), value(val) {}
};
class token_stream // Creating a new class called token_stream
{ // This is where token info can be pulled from
public: // and put back as needed
token_stream();
token get();
void putback(token t);
private:
bool full;
token buffer;
};
token_stream::token_stream() // token_stream function is a member of
:full(false), buffer(0) {} // token_stream class
void token_stream::putback(token t) // putback() function is a member of
{ // token_stream
if (full) error("Can't put back into a full buffer");
buffer = t;
full = true;
}
token token_stream::get() // get() function is a member of token_stream
{ // and returns a token
if (full)
{
full = false;
return buffer;
}
char ch;
cin >> ch;
switch (ch)
{
case '=':
case 'x':
case '{': case '}': case '(': case ')': case '+':
case '-': case '*': case '/': case '!':
return token(ch);
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch);
double val;
cin >> val;
return token('8', val);
}
default:
error("Bad token");
}
}
token_stream ts; // Declare a new token_stream named ts
double expression(); // Declare a new function named expression()
double primary() // Function deals with brackets or
{ // Swaps the character out for it's value
token t = ts.get();
switch (t.kind)
{
case '{':
{
double d = expression();
t = ts.get();
if (t.kind != '}') error("'}' expected");
return d;
}
case '(':
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case '8':
return t.value;
default:
error("Primary expected");
}
}
double factorial() // Factorial function that's not working
{
double left = primary();
token t = ts.get();
while (true)
{
switch (t.kind)
{
case '!':
{
int x = left;
if (x == 0) { left = 1; }
else if (x < 0) error("Factorial must be a positive integer");
else for (; x > 0; x--)
{
x *= x;
left = x;
}
t = ts.get();
break;
}
default:
ts.putback(t);
return left;
}
}
}
double term() // Function deals with multiplication and division
{
double left = factorial();
token t = ts.get();
while (true)
{
switch (t.kind)
{
case '*':
left *= factorial();
t = ts.get();
break;
case '/':
{
double d = factorial();
if (d == 0) error("Can't divide by 0");
left /= d;
t = ts.get();
break;
}
default:
ts.putback(t);
return left;
}
}
}
double expression() // Function deals with addition and subtraction
{
double left = term();
token t = ts.get();
while (true)
{
switch (t.kind)
{
case '+':
left += term();
t = ts.get();
break;
case '-':
left -= term();
t = ts.get();
break;
default:
ts.putback(t);
return left;
}
}
}
int main()
try {
double val = 0;
cout << "Welcome to our simple calculator.\n"
<< "Please enter expressions using floating-point numbers.\n"
<< "Note: Only +, -, *, /, (, ), { and } can be used in the expressions.\n"
<< "Hint: Use = at the end of an expression to get the result and use x at any time to quit.\n"
<< "Example: {2+(2+3-1)}*2/1=\n"
<< '\n';
while (cin)
{
token t = ts.get();
if (t.kind == 'x') break;
else if (t.kind == '=')
cout << "= " << val << '\n';
else
ts.putback(t);
val = expression();
}
keep_window_open();
return 0;
}
catch (exception& e)
{
cerr << "Error: " << e.what() << '\n';
keep_window_open();
return 1;
}
catch (...)
{
cerr << "Oops: Unknown Exception!\n";
keep_window_open();
return 2;
}
| |
The grammar is as follow;
Expression:
Term
Expression "+" Term
Expression "-" Term
Term:
Factorial
Term "*" Factorial
Term "/" Factorial
Factorial:
Primary
Integer "!"
Primary:
Number
"(" Expression ")"
Number:
Floating-point-literal
So the order in which the functions call upon each other is;
Main -> Expression -> Term -> Factorial -> Primary
I know that the factorial() function is mostly working. It will correctly call upon information from primary(). It will correctly give me 1 when I input 0!. It'll give me the error when I input a negative number (eg. -2!). It just doesn't work right with any positive integer other than 1. I tried some numbers and get the following;
-2! = Factorial must be a positive integer (works)
0! = 1 (works)
1! = 1 (works)
2! = -2.1478e+09 (problem)
4! = -1.77744e+09 (problem)
7! = -1.65649e+09 (problem)
I've been at it for hours and this is the closest I've got it to work. Any help would be greatly appreciated.
Thank You.