C++ Programming Language中的Calculator源代码
C++ Programming Language 4th中的Calculator源代码整理,因为在C++ Programming Language中,涉及了很多文件位置之类的变化,所以,这里只是其中的一个版本:
error.h
#include <string> #include <iostream> namespace Error { extern int no_of_errors; inline double error(const std::string& s) { no_of_errors++; std::cerr << "error:" << s << '\n'; return 1; } }
Table.h
#include <map> #include <string> namespace Table { extern std::map<std::string, double> table; }
Lexer.h
#include <string> #include <istream> namespace Lexer { 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(); // read and return next token Token& current() // most recent read token { 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; // pointer to an input stream bool owns; // does the Token_stream own the istream? Token ct{ Kind::end }; // current token }; extern Token_stream ts; }
Parser.h
namespace Parser { double prim(bool get); double term(bool get); double expr(bool get); }
Driver.h
#include <iostream> #include "Lexer.h" #include "Parser.h" namespace Driver { using namespace Lexer; using namespace Parser; inline void calculate() { for (;;) { ts.get(); if (ts.current().kind == Kind::end) break; if (ts.current().kind == Kind::print) continue; std::cout << expr(false) << "\n"; } } }
Error.cpp
#include "Error.h" namespace Error { int no_of_errors = 0; }
Table.cpp
#include "Table.h" namespace Table { std::map<std::string, double> table; }
Lexer.cpp
#include "Lexer.h" #include <iostream> namespace Lexer { Token Token_stream::get() { char ch = 0; do { // skip whitespace except '\n' if (!ip->get(ch)) return ct = { Kind::end }; } while (ch != '\n' && isspace(ch)); switch (ch) { case 0: return ct = { Kind::end }; // assign and return case ';': // end of expression; print 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); // put the first digit (or .) back into the input stream *ip >> ct.number_value; // read number into ct ct.kind = Kind::number; return ct; default: // name, name = , or error if (isalpha(ch)) { ct.string_value = ch; while (ip->get(ch) && isalnum(ch)) ct.string_value += ch; // append ch to end of string_value ip->putback(ch); return ct = { Kind::name }; } } } Token_stream ts{ std::cin }; }
Parser.cpp
#include "Parser.h" #include "Lexer.h" #include "Error.h" #include "Table.h" namespace Parser { using namespace Lexer; using namespace Error; using namespace Table; double expr(bool get) // add and subtract { 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; } } } double term(bool get) // multiply and divide { 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; } } } double prim(bool get) // handle primaries { if (get) ts.get(); // read next token switch (ts.current().kind) { case Kind::number: // floating-point constant { double v = ts.current().number_value; ts.get(); return v; } case Kind::name: { double& v = table[Lexer::ts.current().string_value]; // find the corresponding if (ts.get().kind == Kind::assign) v = expr(true); // '=' seen: assignment return v; } case Kind::minus: // unary minus return -prim(true); case Kind::lp: { auto e = expr(true); if (ts.current().kind != Kind::rp) return error("')' expected"); ts.get(); // eat ')' return e; } default: return error("primary expected"); } } }
实际使用代码(测试代码):
#include "Error.h" #include "Driver.h" #include "Table.h" int main() { Table::table["pi"] = 3.1415926535897932385; // inser t predefined names Table::table["e"] = 2.7182818284590452354; Driver::calculate(); return Error::no_of_errors; }