Created
September 14, 2011 13:44
-
-
Save torazuka/1216579 to your computer and use it in GitHub Desktop.
Chapter07_drill_PPPC++
This file contains hidden or 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
| /* | |
| > calculator08buggy.cpp | |
| > Helpful comments removed. | |
| > We have inserted 3 bugs that the compiler will catch and 3 that it won't. | |
| 7章ドリル. this is an answer. | |
| */ | |
| #include "../../std_lib_facilities.h" | |
| 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) { } // drill(1) 定義がなかったので追加 | |
| }; | |
| class Token_stream { | |
| bool full; | |
| Token buffer; | |
| public: | |
| Token_stream() :full(0), buffer(0) { } | |
| Token get(); | |
| void unget(Token t) { buffer=t; full=true; } | |
| void ignore(char); | |
| }; | |
| const char let = 'L'; | |
| const char declkey = '#'; // drill(5)(10) 定義がなかったので追加、letから#に変更 | |
| const char quit = 'Q'; | |
| const string quitkey = "exit"; // drill(11) | |
| const char print = ';'; | |
| const char number = '8'; | |
| const char name = 'a'; | |
| const char sqrts = 'S'; // drill(7) | |
| const string sqrtkey = "sqrt"; // drill(7) | |
| const char pows = 'P'; // drill(9) | |
| const string powkey = "pow"; // drill(9) | |
| Token Token_stream::get() | |
| { | |
| if (full) { full=false; return buffer; } | |
| char ch; | |
| cin >> ch; | |
| cout << "INFO: 「" << ch << "」\n"; | |
| switch (ch) { | |
| case '(': | |
| case ')': | |
| case '+': | |
| case '-': | |
| case '*': | |
| case '/': | |
| case '%': | |
| case print: // drill(5) print定数があるのに、直接';'と書いてあった | |
| // case quit: // drill(5) 抜けていた (11) Qからexitにするため、再び取り除いた | |
| case ',': // drill(9) カンマ区切りに対応 | |
| case '=': | |
| return Token(ch); | |
| case declkey: | |
| return Token(let); // drill(5)"let"から変更 (10)"#"1文字になったので場所移動 | |
| 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)) { | |
| string s; | |
| s += ch; | |
| while(cin.get(ch) && (isalpha(ch) || isdigit(ch))) s+=ch; // drill(5) s=chになってた | |
| cin.unget(); | |
| if (s == quitkey) return Token(quit); // drill(5) コメントアウト (11) フッカツ | |
| if (s == sqrtkey) return Token(sqrts); // drill(7) sqrtに対応 | |
| if (s == powkey) return Token(pows); // drill(9) powに対応 | |
| 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; | |
| } | |
| struct Variable { | |
| string name; | |
| double value; | |
| Variable(string n, double v) :name(n), value(v) { } | |
| }; | |
| vector<Variable> names; | |
| double get_value(string s) | |
| { | |
| for (int 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 (int 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 (int i = 0; i<names.size(); ++i) | |
| if (names[i].name == s) return true; | |
| return false; | |
| } | |
| Token_stream ts; | |
| double expression(); | |
| double primary() | |
| { | |
| Token t = ts.get(); | |
| switch (t.kind) { | |
| case '(': | |
| { double d = expression(); | |
| t = ts.get(); | |
| if (t.kind != ')') error("')' expected"); // drill(3) "'(' expected"になっていた | |
| } | |
| case '-': | |
| return - primary(); | |
| case number: | |
| return t.value; | |
| case name: | |
| return get_value(t.name); | |
| default: | |
| error("primary expected"); | |
| } | |
| } | |
| double term() | |
| { | |
| 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; | |
| } | |
| default: | |
| ts.unget(t); | |
| return left; | |
| } | |
| } | |
| } | |
| double expression() | |
| { | |
| 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 define_name(string var, double val) | |
| // (var, val) を namesに追加する | |
| { | |
| if(is_declared(var)){ | |
| error(var, " declared twice"); | |
| } | |
| names.push_back(Variable(var, val)); | |
| return val; | |
| } | |
| double declaration() | |
| { | |
| Token t = ts.get(); | |
| // if (t.kind != 'a') error ("name expected in declaration"); // drill 5 | |
| if (t.kind != name) error ("name expected in declaration"); | |
| string name = t.name; | |
| if (is_declared(name)) error(name, " declared twice"); | |
| Token t2 = ts.get(); | |
| if (t2.kind != '=') error("= missing in declaration of " ,name); | |
| double d = expression(); | |
| // names.push_back(Variable(name,d)); // drill(5) | |
| define_name(name, d); // drill(5) | |
| return d; | |
| } | |
| double calcu_sqrt() | |
| // drill(7) 標準ライブラリ関数sqrtを使った計算に対応する | |
| // sqrtが検出されていることを前提とする | |
| // '(' expression ')' | |
| { | |
| Token t = ts.get(); | |
| if(t.kind != '('){ | |
| error("'(' expected"); | |
| } | |
| double d = expression(); | |
| if(d < 0){ // drill(8) 負の数が入力された場合のエラー処理 | |
| error("sqrt: 平方根を計算するときは正の数を入力してください。"); | |
| } | |
| Token t2 = ts.get(); | |
| if(t2.kind != ')'){ | |
| error("')' expected"); | |
| } | |
| return sqrt(d); | |
| } | |
| double calcu_pow() | |
| // drill(9) pow(x, count)で、xをcount回掛ける | |
| // powが検出されていることを前提とする | |
| // '(' expression ',' expression ')' | |
| { | |
| Token t = ts.get(); | |
| if(t.kind != '('){ | |
| error("'(' expected"); | |
| } | |
| double x = expression(); | |
| Token t2 = ts.get(); | |
| if(t2.kind != ','){ | |
| error("',' expected"); | |
| } | |
| double d = expression(); | |
| int count = int(d); | |
| if(count != d){ | |
| error("pow(x, i)のとき、iは整数でなければいけません。"); | |
| } | |
| Token t3 = ts.get(); | |
| if(t3.kind != ')'){ | |
| error("')' expected"); | |
| } | |
| double result = x; | |
| for(int j = 1; j < count; j++){ | |
| result *= x; | |
| } | |
| return result; | |
| } | |
| double statement() | |
| { | |
| Token t = ts.get(); | |
| switch(t.kind) { | |
| case let: | |
| return declaration(); | |
| case sqrts: // drill(7) | |
| return calcu_sqrt(); | |
| case pows: // drill(9) | |
| return calcu_pow(); | |
| default: | |
| ts.unget(t); | |
| return expression(); | |
| } | |
| } | |
| void clean_up_mess() | |
| { | |
| ts.ignore(print); | |
| } | |
| const string prompt = "> "; | |
| const string result = "= "; | |
| void calculate() | |
| { | |
| // while(true) try { // drill(5) | |
| while(cin) try { | |
| cout << prompt; | |
| Token t = ts.get(); | |
| while (t.kind == print) t=ts.get(); | |
| if (t.kind == quit) return; | |
| ts.unget(t); | |
| cout << result << statement() << endl; | |
| } | |
| catch(runtime_error& e) { | |
| cerr << e.what() << endl; | |
| clean_up_mess(); | |
| } | |
| } | |
| int main() | |
| { | |
| try { | |
| // 事前に値を定義する // drill(6) | |
| define_name("k", 1000); | |
| 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
Put "case sqrts:" in "primary()" so it can calculate "sqrt(4)*3;".