Skip to content

Instantly share code, notes, and snippets.

@anduix
Last active September 20, 2018 17:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anduix/067340644b664521a69e89de970a1cb5 to your computer and use it in GitHub Desktop.
Save anduix/067340644b664521a69e89de970a1cb5 to your computer and use it in GitHub Desktop.
/*
# 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