Last active
March 5, 2021 11:49
-
-
Save jatinsharrma/92054ce5a40429b5310ffbfd443ea68a to your computer and use it in GitHub Desktop.
Calculator | C++ | Programming -- Principles and Practice Using C++ by Bjarne Stroustrup
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
/* | |
The grammar for input is: | |
Calculation : | |
Statement | |
Quit | |
Calculation Statement | |
Statement: | |
Declaration | |
Expression | |
Declaration: | |
"let" Name "=" Expression | |
Statement: | |
Expression | |
Quit | |
Print : | |
; | |
Quit : | |
q | |
Expression: | |
Term | |
Expression + Term | |
Expression - Term | |
Term: | |
Primary | |
Term * Primary | |
Term / Primary | |
Term % Primary | |
Primary: | |
Number | |
( Expression ) | |
- Primary | |
+ Primary | |
Number: | |
floalting-point-literal | |
Input comes from cin through the Token_stream called ts. | |
*/ | |
//============================================================= | |
// Features : +, - ,/ , * , % , variables | |
// Bugs : if you just type ';' you will prompted to type again and again eg, ;;;;;;;;;;;;;;;;;;;;;................ | |
// if your stream of ';' is followed by expression then it is ok. eg ;;;;;;;;;;;;;;;;;;;;; 1+1; | |
// How to use: | |
// declaring variable : let var1 = 10; | |
// end all expression with ';' | |
// calling variable : var1 = 10; | |
#include <iostream> | |
#include <vector> | |
using namespace std; | |
// all functions declarations | |
double statement(); | |
double declaration(); | |
double expression(); | |
double term(); | |
double primary(); | |
double get_value(string s); | |
double define_name(string var, double val); | |
bool is_declared(string var); | |
void calculate(); | |
void clean_up_mess(); | |
void set_value(string, double); | |
void error(string); | |
//constants | |
const string prompt = "> "; | |
const string result = "= "; | |
const string declkey = "let"; | |
const char name = 'a'; | |
const char let = 'L'; | |
const char quit = 'q'; | |
const char print = ';'; | |
const char number = '8'; | |
class Token | |
{ | |
public: | |
char kind; // type of token | |
double value; // value of token | |
string name; // name of variable | |
//constructor for operators | |
Token(char ch) | |
:kind(ch), value(0){} | |
//constructor for operands | |
Token(char ch, double val) | |
:kind(ch), value(val){} | |
//construnctor for variable | |
Token (char ch, string n) | |
: kind(ch), name(n){} | |
}; | |
class Token_stream | |
{ | |
private: | |
bool full; // flag for buffer status, empty or full | |
Token buffer; // buffer stores only one token | |
public: | |
Token_stream() :full(false), buffer(0){}; | |
Token get(); // read from console | |
void putback(Token t); // save into buffer | |
void ignore(char c); // cleaning up mess, making calculator ready for new calculation | |
}; | |
void Token_stream::putback(Token t) | |
{ | |
if(full) error("putback() into a full buffer."); | |
buffer = t; //copy t to buffer | |
full = true; //buffer is now full | |
} | |
Token Token_stream :: get() | |
{ | |
if (full){ // check if we have a token already. | |
//remove token from buffer | |
full = false; | |
return buffer; | |
} | |
// if buffer is empty | |
char ch; | |
cin >> ch; // reading new token | |
switch (ch) | |
{ | |
case print: | |
case quit: | |
case '(': | |
case ')': | |
case '+': | |
case '-': | |
case '*': | |
case '/': | |
case '%': | |
case '=': | |
return Token(ch); | |
break; | |
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(number,val); | |
default: | |
if (isalpha(ch)){ | |
cin.putback(ch); | |
string s = ""; | |
while(cin.get(ch) && isalpha(ch) || isdigit(ch)) s+=ch; | |
cin.putback(ch); | |
if(s == declkey) return Token(let); | |
return Token(name,s); | |
} | |
error("Bad Token"); | |
break; | |
} | |
} | |
void Token_stream :: ignore(char c) | |
{ | |
// first look in buffer | |
if (full && c == buffer.kind){ | |
full = false; | |
return; | |
} | |
full = false; | |
// now search input | |
char ch = 0; | |
while (cin >> ch) | |
if (ch == c) return; | |
} | |
class Variable{ | |
public : | |
string name; // variable name | |
double value; // variable value | |
Variable (string n, double v) | |
: name(n), value(v){} | |
}; | |
// Token_stream object | |
Token_stream ts; | |
// vector table for variables | |
vector <Variable> var_table; | |
int main() | |
{ | |
try{ | |
calculate(); | |
return 0; | |
} | |
catch(runtime_error& e){ | |
cerr << e.what() << endl; | |
return 1; | |
} | |
catch(...){ | |
cerr <<"Somthing went wrong"; | |
return 2; | |
} | |
} | |
//expression evaluation loop | |
void calculate(){ | |
while(cin){ | |
try{ | |
cout << prompt; | |
Token t = ts.get(); | |
while (t.kind == print) t= ts.get(); //first discard all "prints" | |
if (t.kind == quit) return; // Press q to quit | |
ts.putback(t); | |
cout << result << statement() << endl; | |
} | |
catch(exception& e){ | |
cerr << e.what()<<endl; | |
clean_up_mess(); | |
} | |
} | |
} | |
// Statement, grammer starts from here | |
double statement() | |
{ | |
Token t = ts.get(); | |
switch (t.kind){ | |
case let: | |
return declaration(); | |
default: | |
ts.putback(t); | |
return expression(); | |
} | |
} | |
// Primary | |
double primary() | |
{ | |
Token t = ts.get(); | |
switch (t.kind){ | |
case '(' : | |
{ | |
double d = expression(); | |
t = ts.get(); | |
if(t.kind != ')') error("')' expected"); | |
return d; | |
} | |
case number : | |
return t.value; // return number's value | |
break; | |
case '-': | |
return -term(); // returing uninary minus value | |
case '+': | |
return term(); | |
case name: | |
{ | |
Token next = ts.get(); | |
if (next.kind == '=') { | |
double d = expression(); | |
set_value(t.name,d); | |
return d; | |
} | |
ts.putback(next); | |
return get_value(t.name); | |
} | |
default: | |
ts.putback(t); // to make program ready for new expression | |
// if you don't put this most probably ';' this will be eaten up and your program wait for that again. | |
error("primary expected"); | |
} | |
} | |
// Term | |
double term() | |
{ | |
double left = primary(); | |
Token t = ts.get(); | |
while (true){ | |
switch (t.kind){ | |
case '*': | |
left *= primary(); | |
t = ts.get(); | |
break; | |
case '/': | |
{ | |
double d = primary(); | |
if (d == 0) error("divided by zero"); | |
left /= d; | |
t = ts.get(); | |
break; | |
} | |
case '%':{ | |
double right = primary(); | |
int i1 = int(right); | |
if (i1 != right) error("right-hand operand of % not int"); | |
int i2 = int(left); | |
if (i2 != left) error ("left-hand operand of % not int"); | |
if (i1 == 0) error("%: divide by zero"); | |
left = i2 % i1; | |
t= ts.get(); | |
break; | |
} | |
default : | |
ts.putback(t); | |
return left; | |
} | |
} | |
} | |
// Expression | |
double expression() | |
{ | |
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; | |
} | |
} | |
} | |
// cleanaing up the mess | |
void clean_up_mess() | |
{ | |
ts.ignore(print); | |
} | |
// return the value of the variable named as s | |
double get_value(string s) | |
{ | |
for(int i=0; i < var_table.size(); i++) | |
if (var_table[i].name == s ) return var_table[i].value; | |
error("get:undefined variable '" + s + "'"); | |
} | |
// set the varaible named s to d | |
void set_value(string s, double d) | |
{ | |
for(int i=0; i<var_table.size(); i++) | |
if (var_table[i].name == s){ | |
var_table[i].value = d; | |
return; | |
} | |
error("set:undefined varaible '" + s + "'"); | |
} | |
// is var already in var_table? | |
bool is_decalred (string var) | |
{ | |
for (int i =0; i<var_table.size(); i++) | |
if (var_table[i].name == var) return true; | |
return false; | |
} | |
// saving variable to var_table | |
double define_name(string var, double val) | |
{ | |
if (is_decalred(var)) error(var+" decalred twice"); | |
var_table.push_back(Variable(var,val)); | |
return val; | |
} | |
// declaring new variable | |
double declaration() | |
{ | |
Token t = ts.get(); | |
if (t.kind != name) error("name expected in declaration"); | |
string var_name = t.name; | |
Token t2 = ts.get(); | |
if(t2.kind != '=') error ("= missing in declaration" + var_name); | |
double d = expression(); | |
define_name(var_name, d); | |
return d; | |
} | |
//error | |
inline void error(string st) | |
{ | |
throw runtime_error(st); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment