http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h
Chapter 8 // Exercise 1
1. Modify the calculator program from Chapter 7 to make the input stream an explicit parameter (as shown in section 8.5.8), rather than simple using cin. Also give the Token_stream constructor (section 7.8.2) an istream& parameter so that when we figure out how to make out own istream (e.g., attached to files), we can use the calculator for those. Hint: Don't try to copy an istream.
// pandp.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "std_lib_facilities.h" #include <windows .h> //user defined type to hold name-value pair for use in calculator struct Token { 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 n) :kind(ch), name(n) { } }; //user-defined type that handles retrieving items from input class Token_stream { bool full; Token buffer; public: Token_stream() :full(0), buffer(0) { } //default constructor Token_stream(istream&); //constructor for istream Token get(); void unget(Token t) { buffer = t; full = true; } void ignore(char); }; double expression(Token_stream& ts); //forward declaration void calculate(Token_stream& ts); void showHelp(); const char let = 'L'; const char quit = 'Q'; const char print = '\n'; const char number = '8'; const char name = 'a'; const char squareR = 's'; const char findPow = 'p'; const char isConst = 'C'; const char help = 'h'; const string declKey = "let"; const string quitKey = "quit"; const string sqrtKey = "sqrt"; const string powKey = "pow"; const string constKey = "const"; const string printKey = "'\n'"; //evaluate each char in the stream and determine what it is Token Token_stream::get() { if (full) //check if we have already have a token ready { full = false; return buffer; } char ch; cin.get(ch); //does not skip whitespace while (isspace(ch)) //if ch is whitespace { if (ch == '\n') //if ch == newline return Token(print); //print result cin.get(ch); //if not newline get next ch in stream } switch (ch) { case '(': case ')': case '+': case '-': case '*': case '/': case '%': case '=': case ',': return Token(ch); //let each char represent itself case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { cin.unget(); //put digit back into the input stream double val; cin >> val; //read a floating-point number return Token{ number, val }; //return number or . with a value, put back into buffer } //allow user defined variables if user types # case '#': return Token(let); //if user presses h or H return to execute help function case 'h': case 'H': return Token(help); default: //do this if ch is a letter if (isalpha(ch) || ch == '_') { string s; s += ch; //while there are still chars in cin, read them into s while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_')) s += ch; cin.unget(); //if string is equal to other commands defined below, return them if (s == declKey) return Token(let); if (s == constKey) return Token(isConst); if (s == quitKey) return Token(quit); if (s == sqrtKey) return Token(squareR); if (s == powKey) return Token(findPow); return Token(name, s); } //if the char does not fit any of these paramenters return an error message error("Bad token"); } } //discard characters up to and including a c //c represents the kind of token void Token_stream::ignore(char c) { //first look in the buffer if (full && c == buffer.kind) { full = false; return; } full = false; //now search input char ch; while (cin >> ch) if (ch == c) return; } //-----------------------------------------------------------------------------------------------------// //-----------------------------------------------------------------------------------------------------// struct Variable { string name; double value; bool isConst; Variable(string n, double v, bool ic) :name(n), value(v), isConst(ic) { } }; //-----------------------------------------------------------------------------------------------------// class Symbol_table { vector<Variable> var_table; public: double get(string s); void set(string s, double d); bool is_declared(string s); double define(string var, double val, bool isConst); double declare(Token_stream& ts); }; //return the value of the Variable named s double Symbol_table::get(string s) { for (int i = 0; i < Symbol_table::var_table.size(); ++i) { if (Symbol_table::var_table[i].name == s) { return Symbol_table::var_table[i].value; } } error("get: undefined name ", s); } //set the Variable named s to d void Symbol_table::set(string s, double d) { for (int i = 0; i < Symbol_table::var_table.size(); ++i) { //allow redefinitions as long as variable isn't const if (Symbol_table::var_table[i].name == s && Symbol_table::var_table[i].isConst == false) { Symbol_table::var_table[i].value = d; return; } } error("set: undefined name ", s); } //is variable already declared? bool Symbol_table::is_declared(string s) { for (int i = 0; i < Symbol_table::var_table.size(); ++i) { if (Symbol_table::var_table[i].name == s && Symbol_table::var_table[i].isConst == true) error("Cannot reassign const variable"); else if (Symbol_table::var_table[i].name == s && Symbol_table::var_table[i].isConst == false) return true; } return false; } //allow programmers to add (var,val) to variable vector double Symbol_table::define(string var, double val, bool isConst) { if (is_declared(var)) error(var, " declared twice"); var_table.push_back(Variable(var, val, isConst)); return val; } //check for name definition errors double Symbol_table::declare(Token_stream& ts) { Token t = ts.get(); //is const the next word in the stream? bool isC; if (t.kind == isConst) { isC = true; t = ts.get(); //get the the next word in the stream for the name } else isC = false; if (t.kind != name) error("name expected in declaration;"); string name = t.name; //if name has already been declared ask if they want to change it if (Symbol_table::is_declared(name)) { cout << name + ", declared twice. Would you like to reassign? (No need to print with ';') y/n > "; cin.clear(); cin.ignore(10000, '\n'); //clear the buffer string ans; getline(cin, ans); if (ans == "n") error(name, ", will not be reassigned; "); if (ans == "y") { cout << "(No need to print with ';') Please enter new value: "; int val; cin >> val; Symbol_table::set(name, val); double d = val; //return value to print to reset calculator return d; } } Token t2 = ts.get(); if (t2.kind != '=') error("= missing in declaration of ", name); double d = expression(ts); Symbol_table::var_table.push_back(Variable(name, d, isC)); return d; } //-----------------------------------------------------------------------------------------------------// Symbol_table symbols; //-----------------------------------------------------------------------------------------------------// //check tokenstream for 'your char here' Token checkForChar(Token t, char ch) { if (t.kind != ch) { //convert ch to string for error message string chstring = ""; chstring += ch; error("'" + chstring + "' expected"); } return t; } //solve characters received from ts.get() double primary(Token_stream& ts) { //get character from stream Token t = ts.get(); switch (t.kind) { //solve "(expression)" case '(': { double d = expression(ts); t = ts.get(); checkForChar(t, ')'); return d; } //solve "-primary" case '-': return -primary(ts); //solve "number" case number: return t.value; //solve "name" case name: return symbols.get(t.name); //solve "sqrt(expression)" case squareR: { //get next char after 'sqrt' if not '(' then error t = ts.get(); checkForChar(t, '('); //if expression is less than 0 print an error double d = expression(ts); if (d < 0) error("Cannot squareroot negative integers"); //get next char after expression, if not ')' then error t = ts.get(); checkForChar(t, ')'); // return square root of the expression taken from the tokenstream return sqrt(d); } //solve "pow(expression, expression)" case findPow: { //get next char after 'pow' if not '(' then error t = ts.get(); checkForChar(t, '('); //get the expression after '(' double d = expression(ts); //get next char after 'expression' if not ',' then error t = ts.get(); checkForChar(t, ','); //get the expression after ',' double i = expression(ts); //get next char after expression, if not ')' then error t = ts.get(); checkForChar(t, ')'); // return expression using pow() fromreturn pow(d, i); } default: error("primary expected"); } } //solves for primary, *, / and % double term(Token_stream& ts) { double left = primary(ts); while (true) { Token t = ts.get(); switch (t.kind) { case '*': left *= primary(ts); break; case '/': { double d = primary(ts); if (d == 0) error("divide by zero"); left /= d; break; } case '%': { double d = primary(ts); if (d == 0) error("%:divide by zero"); left = fmod(left, d); break; } default: ts.unget(t); return left; } } } //solves for terms and + - double expression(Token_stream& ts) { double left = term(ts); while (true) { Token t = ts.get(); switch (t.kind) { case '+': left += term(ts); break; case '-': left -= term(ts); break; default: ts.unget(t); return left; } } } double statement(Token_stream& ts) { Token t = ts.get(); switch (t.kind) { case let: return symbols.declare(ts); default: ts.unget(t); return expression(ts); } } void clean_up_mess() { cout << "\nRestarting Calculator ."; Sleep(400); cout << "."; Sleep(400); cout << "."; Sleep(400); cout << "."; Sleep(400); cout << ".\n\n"; cin.clear(); cin.ignore(10000, '\n'); //clear the buffer } const string prompt = "> "; const string result = "= "; void calculate(Token_stream& ts) { while (true) try { cout << prompt; Token t = ts.get(); while (t.kind == print) t = ts.get(); //first discard all 'prints' //if user types h or H show help, then clear the stream and get new values if (t.kind == help) { showHelp(); cout << prompt; t = ts.get(); } if (t.kind == quit) return; ts.unget(t); cout << result << statement(ts) << endl; } catch (runtime_error& e) { cerr << e.what() << endl; clean_up_mess(); } } int main() try { symbols.define("pi", 3.1415926535, true); symbols.define("e", 2.7182818284, true); symbols.define("k", 1000, false); Token_stream ts; calculate(ts); 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; } void showHelp() { cout << "-----------------------HOW TO USE THE CALCULATOR------------------------\n"; cout << "------------------------------------------------------------------------\n"; cout << "1. To exit the program type 'quit' and press enter.\n"; cout << "2. To show results, press enter.\n"; cout << "3. To create your own keyword, type 'let yourWord = value'\n"; cout << " You can let a keyword be a const by typing 'const' before 'let'\n"; cout << "4. To find the squareroot, type 'sqrt' followed by your number.\n"; cout << "5. To find a number to a power of, type 'pow(number,number)'\n"; cout << "------------------------------------------------------------------------\n\n"; cin.clear(); cin.ignore(10000, '\n'); //clear the buffer for new values }
No comments:
Post a Comment