Created
November 2, 2019 17:51
-
-
Save wagavulin/5775542e0aff24fff5d8415d98b4013b to your computer and use it in GitHub Desktop.
プログラミング言語C++ 第4版 10.2 電卓プログラム
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
#include <iostream> | |
#include <string> | |
#include <map> | |
#include <cctype> | |
// プログラミング言語C++ 第4版 10.2 電卓プログラム | |
// echo "1+2" | ./a.out | |
enum class Kind : char { | |
name, | |
number, | |
end, | |
plus = '+', | |
minus = '-', | |
mul = '*', | |
div = '/', | |
print = ';', | |
assign = '=', | |
lp = '(', | |
rp = ')' | |
}; | |
struct Token { | |
Kind kind; | |
std::string string_value; | |
double number_value; | |
}; | |
class Token_stream { | |
public: | |
Token_stream(std::istream& s) : ip{&s}, owns{false} {} | |
Token_stream(std::istream* p) : ip{p}, owns{true} {} | |
~Token_stream() { close(); } | |
Token get(); // 次のトークンを読み取って返却 | |
const Token& current(){ return ct; } // 最後に読みこんだトークン | |
void set_input(std::istream& s) { close(); ip = &s; owns = false; } | |
void set_input(std::istream* p) { close(); ip = p; owns = true; } | |
private: | |
void close() { if (owns) delete ip; } | |
std::istream* ip; | |
bool owns; // Token_streamはistreamを所有しているか | |
Token ct {Kind::end}; // カレントトークン | |
}; | |
int no_of_errors; | |
Token_stream ts {std::cin}; | |
std::map<std::string, double> table; | |
double expr(bool); | |
double error(std::string s){ | |
no_of_errors++; | |
std::cerr << "error: " << s << std::endl; | |
return 1; | |
} | |
Token Token_stream::get(){ | |
char ch = 0; | |
do { // '\n'以外の空白をスキップ | |
if (!ip->get(ch)) return ct = {Kind::end}; | |
} while (ch != '\n' && isspace(ch)); | |
switch (ch) { | |
case ';': case '\n': | |
return ct = {Kind::print}; | |
case '*': case '/': case '+': case '-': | |
case '(': case ')': case '=': | |
return ct = {static_cast<Kind>(ch)}; | |
case '0': case '1': case '2': case '3': case '4': | |
case '5': case '6': case '7': case '8': case '9': | |
case '.': | |
ip->putback(ch); | |
*ip >> ct.number_value; | |
ct.kind = Kind::number; | |
return ct; | |
default: | |
if (isalpha(ch)) { | |
ct.string_value = ch; | |
while (ip->get(ch)) { | |
if (isalnum(ch)) | |
ct.string_value += ch; | |
else { | |
ip->putback(ch); | |
break; | |
} | |
} | |
ct.kind = {Kind::name}; | |
return ct; | |
} | |
} | |
error("bad token"); | |
return ct = {Kind::print}; | |
} | |
double prim(bool get){ | |
if (get) ts.get(); | |
switch (ts.current().kind) { | |
case Kind::number: | |
{ | |
double v = ts.current().number_value; | |
ts.get(); | |
return v; | |
} | |
case Kind::name: | |
{ | |
double&v = table[ts.current().string_value]; | |
if (ts.get().kind == Kind::assign) | |
v = expr(true); | |
return v; | |
} | |
case Kind::minus: // 単項のマイナス | |
return -prim(true); | |
case Kind::lp: | |
{ | |
auto e = expr(true); | |
if (ts.current().kind != Kind::rp) | |
return error("')' expected"); | |
ts.get(); // ')'を読み飛ばす | |
return e; | |
} | |
default: | |
return error("primary expected"); | |
} | |
return 0.0; | |
} | |
double term(bool get){ | |
double left = prim(get); | |
for (;;) { | |
switch (ts.current().kind) { | |
case Kind::mul: | |
left *= prim(true); | |
break; | |
case Kind::div: | |
if (auto d = prim(true)) { | |
left /= d; | |
break; | |
} | |
return error("divide by 0"); | |
default: | |
return left; | |
} | |
} | |
return -9999.0; | |
} | |
double expr(bool get){ | |
double left = term(get); | |
for (;;) { | |
switch (ts.current().kind) { | |
case Kind::plus: | |
left += term(true); | |
break; | |
case Kind::minus: | |
left -= term(true); | |
break; | |
default: | |
return left; | |
} | |
} | |
return -9999.0; | |
} | |
void calculate(){ | |
for (;;) { | |
ts.get(); | |
if (ts.current().kind == Kind::end) break; | |
if (ts.current().kind == Kind::print) continue; | |
std::cout << expr(false) << std::endl; | |
} | |
} | |
int main(){ | |
table["pi"] = 3.1415926535897932385; | |
table["e"] = 2.7182818284590452354; | |
calculate(); | |
return no_of_errors; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment