Last active
September 20, 2018 17:35
-
-
Save anduix/067340644b664521a69e89de970a1cb5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
# CALCULATOR PROGRAM | |
# | |
# This calculator program handles addition (+), substraction (-), multiplication (*), division (/), modulo (%), exponentiation (pow(base, exponent)), | |
# square root (sqrt(expression)), creating user-defined variables or constants and editing those user-defined variables. | |
# | |
# Type "help" for help regarding other commands. | |
# | |
# | |
# The general grammar of the program: | |
# | |
# Expression: | |
# Term | |
# Expression + Term | |
# Expression - Term | |
# | |
# Term: | |
# Primary | |
# Term() * Primary | |
# term() / Primary | |
# Term % Primary | |
# | |
# Primary: | |
# Number | |
# ( Expression ) | |
# sqrt(Expression) | |
# pow(base, exponent) | |
# - Primary -> negative number | |
# name -> of either a variable or a constant | |
# } | |
*/ | |
#include "pch.h" | |
#include "D:\Programming\Additionals\std_lib_facilities.h" | |
const char let = 'L'; | |
const char quit = 'Q'; | |
const char print = ';'; | |
const char number = '8'; | |
const char name = 'a'; | |
const char squareroot = 's'; | |
const char power = 'p'; | |
const char con = 'C'; | |
const char help = 'h'; | |
const string helpkey = "help"; | |
const string declkey = "#"; | |
const string quitkey = "exit"; | |
const string constkey = "const"; | |
class Token { | |
public: | |
char kind; | |
double value; | |
string name; | |
Token(char ch) :kind{ ch }, value{ 0 } { } | |
Token(char ch, double val) :kind{ ch }, value{ val } { } | |
Token(char ch, string str) :kind{ ch }, name{ str } { } | |
}; | |
class Token_stream { | |
public: | |
Token_stream() :full{ 0 }, buffer{ 0 } { } | |
Token get(); | |
void unget(Token t) { buffer = t; full = true; } // puts a Token back | |
void ignore(char c); // discards Tokens up to and including c | |
private: | |
bool full; | |
Token buffer; | |
}; | |
Token Token_stream::get() // reads characters from cin and composes Tokens | |
{ | |
if (full) { full = false; return buffer; } | |
char ch; | |
cin.get(ch); | |
if (isspace(ch)) | |
if (ch == '\n') | |
return Token(print); | |
cin.unget(); | |
cin >> ch; | |
switch (ch) { | |
case '(': | |
case ')': | |
case '+': | |
case '-': | |
case '*': | |
case '/': | |
case '%': | |
case '=': | |
case ',': | |
case print: | |
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.unget(); | |
double val; | |
cin >> val; | |
return Token(number, val); | |
} | |
default: | |
if (isalpha(ch) || ch == '#') { | |
string s; | |
s += ch; | |
while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_')) s += ch; | |
cin.unget(); | |
if (s == helpkey) return Token(help); | |
if (s == declkey) return Token(let); | |
if (s == quitkey) return Token(quit); | |
if (s == "sqrt") return Token(squareroot); | |
if (s == "pow") return Token(power); | |
if (s == constkey) return Token(con); | |
return Token(name, s); | |
} | |
error("Bad token"); | |
} | |
} | |
void Token_stream::ignore(char c) | |
{ | |
if (full && c == buffer.kind) { | |
full = false; | |
return; | |
} | |
full = false; | |
char ch; | |
while (cin >> ch) | |
if (ch == c) return; | |
} | |
class Symbol_table { | |
public: | |
string name; | |
double value; | |
bool var; | |
Symbol_table(string n, double v, bool va) :name(n), value(v), var(va) { } | |
}; | |
vector<Symbol_table> names; | |
double get_value(string s) | |
{ | |
for (unsigned i = 0; i < names.size(); ++i) | |
if (names[i].name == s) return names[i].value; | |
error("get: undefined name ", s); | |
} | |
void set_value(string s, double d) | |
{ | |
for (unsigned i = 0; i <= names.size(); ++i) | |
if (names[i].name == s) { | |
names[i].value = d; | |
return; | |
} | |
error("set: undefined name ", s); | |
} | |
bool is_declared(string s) | |
{ | |
for (unsigned i = 0; i < names.size(); ++i) | |
if (names[i].name == s) return true; | |
return false; | |
} | |
Token_stream ts; | |
double expression(); // analyzes input such as addition layout and substraction layout | |
double primary() // analyzes input such as positive or negative numbers, expressions between parantheses, square root layout, exponentiation layout, names of variables / constants | |
{ | |
Token t = ts.get(); | |
switch (t.kind) { | |
case '(': | |
{ | |
double d = expression(); | |
t = ts.get(); | |
if (t.kind != ')') error("')' expected"); | |
break; | |
} | |
case squareroot: // layout: 's' + '(' + expression + ')' | |
{ | |
t = ts.get(); | |
if (t.kind != '(') error("'(' expected"); | |
double d = expression(); | |
if (d < 0) error("sqrt: the number must be positive"); | |
t = ts.get(); | |
if (t.kind != ')') error("')' expected"); | |
return sqrt(d); | |
} | |
case power: // layout: 'p' + '(' + base + ',' + exponent + ')' | |
{ | |
t = ts.get(); | |
if (t.kind != '(') error("'(' expected"); | |
double base = expression(); | |
t = ts.get(); | |
if (t.kind != ',') error("',' expected"); | |
int exponent = narrow_cast<int>(expression()); | |
t = ts.get(); | |
if (t.kind != ')') error("')' expected"); | |
return pow(base, exponent); | |
} | |
case '-': | |
return -primary(); | |
case number: | |
return t.value; | |
case name: | |
{ | |
Token t_next = ts.get(); | |
if (t_next.kind == '=') // handles name = expression | |
{ | |
for (Symbol_table& x : names) | |
if (x.name == t.name) | |
if (x.var) | |
{ | |
double new_value = expression(); | |
set_value(t.name, new_value); | |
return new_value; | |
} | |
else error("can't change the value of a const"); | |
} | |
ts.unget(t_next); | |
return get_value(t.name); | |
} | |
default: | |
error("primary expected"); | |
} | |
} | |
double term() // analyzes input such as multiplication layout, division layout, modulo layout | |
{ | |
double left = primary(); | |
while (true) { | |
Token t = ts.get(); | |
switch (t.kind) { | |
case '*': | |
left *= primary(); | |
break; | |
case '/': | |
{ double d = primary(); | |
if (d == 0) error("divide by zero"); | |
left /= d; | |
break; | |
} | |
case '%': | |
{ | |
int i1 = narrow_cast<int>(left); | |
int i2 = narrow_cast<int>(primary()); | |
if (i2 == 0) error("%: divide by zero"); | |
left = i1 % i2; | |
break; | |
} | |
default: | |
ts.unget(t); | |
return left; | |
} | |
} | |
} | |
double expression() // analyzes input such as addition layout and substraction layout | |
{ | |
double left = term(); | |
while (true) { | |
Token t = ts.get(); | |
switch (t.kind) { | |
case '+': | |
left += term(); | |
break; | |
case '-': | |
left -= term(); | |
break; | |
default: | |
ts.unget(t); | |
return left; | |
} | |
} | |
} | |
double declaration() | |
{ | |
Token t = ts.get(); | |
if (t.kind != name) error("name expected in declaration"); | |
string new_name = t.name; | |
if (is_declared(new_name)) error(new_name, " declared twice"); | |
Token t2 = ts.get(); | |
if (t2.kind != '=') error("= missing in declaration of ", new_name); | |
double d = expression(); // calls expression() for parsing the value of the new variable | |
names.push_back(Symbol_table(new_name, d, true)); | |
return d; | |
} | |
double const_declaration() | |
{ | |
Token t = ts.get(); | |
if (t.kind != name) error("name expected in declaration"); | |
string new_name = t.name; | |
if (is_declared(new_name)) error(new_name, " declared twice"); | |
Token t2 = ts.get(); | |
if (t2.kind != '=') error("= missing in declaration of ", new_name); | |
double d = expression(); // calls expression() for parsing the value of the new constant | |
names.push_back(Symbol_table(new_name, d, false)); | |
return d; | |
} | |
void predefine_name(string name, double value, bool var) | |
{ | |
if (is_declared(name)) error(name, " declared twice"); | |
names.push_back(Symbol_table(name, value, var)); | |
} | |
double statement() // analyzes input such as variable / constant declaration keyword ('#' or "const") - resulting in calling appropriate functions | |
{ | |
Token t = ts.get(); | |
switch (t.kind) { | |
case let: | |
return declaration(); | |
case con: | |
return const_declaration(); | |
default: | |
ts.unget(t); | |
return expression(); | |
} | |
} | |
void clean_up_mess() | |
{ | |
ts.ignore(print); // discards Tokens up to and including the print character | |
} | |
void display_info() | |
{ | |
cout << " This calculator program handles addition (+), substraction (-), multiplication (*), division (/), modulo (%), " << endl | |
<< " exponentiation (pow(base, exponent)), square root(sqrt(expression)), creating user - defined variables or" << endl | |
<< " constants and editing those user - defined variables." << endl | |
<< endl << " Other commands:" << endl | |
<< "\t 'exit' for exiting the program" << endl << "\t '#' keyword for user-defined variable declaration" << endl << "\t 'const' keyword for user-defined constant declaration" << endl; | |
} | |
const string prompt = "> "; | |
const string result = "= "; | |
void calculate() | |
{ | |
while (true) try { | |
cout << prompt; | |
Token t = ts.get(); | |
while (t.kind == print) t = ts.get(); | |
if (t.kind == quit) return; | |
if (t.kind == help) | |
display_info(); | |
else | |
{ | |
ts.unget(t); | |
cout << result << statement() << endl; | |
} | |
} | |
catch (runtime_error& e) { | |
cerr << e.what() << endl; | |
clean_up_mess(); | |
} | |
} | |
int main() | |
try { | |
predefine_name("pi", 3.14159265, false); | |
predefine_name("e", 2.71828182, false); | |
display_info(); | |
calculate(); | |
return 0; | |
} | |
catch (exception& e) { | |
cerr << "exception: " << e.what() << endl; | |
char c; | |
while (cin >> c && c != ';'); | |
return 1; | |
} | |
catch (...) { | |
cerr << "exception\n"; | |
char c; | |
while (cin >> c && c != ';'); | |
return 2; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment