C++で電卓を作った
字句解析と構文解析のアルゴリズムは分かってたけどC++の基本とかunique_ptrが何もわからないからめちゃめちゃ手こずった。
これで合ってるというか見やすいのかわかんないから誰かコードレビューしてくれないかな。他力本願。
という気持ちでソースコードだけ公開。特に意味はない。
#include <iostream>
#include <string>
#include <vector>
#include <cctype>
#include <cstdlib>
#include <sstream>
#include <memory>
using std::unique_ptr;
template <typename U, typename T>
unique_ptr<U> dynamic_unique_cast(unique_ptr<T>&& ptr){
return unique_ptr<U>(dynamic_cast<U*>(ptr.release()));
}
enum class TokenType{
INTEGER,
BINARY_OP1,
BINARY_OP2,
LP,
RP,
};
struct Token {
TokenType type;
std::string value;
Token(TokenType t, std::string v) : type(t), value(v) {}
};
enum class AstType{
INTEGER,
BINARY_OP,
};
struct Ast {
AstType type;
virtual ~Ast() {}
};
struct Integer : Ast {
int value;
Integer(int v) : value(v) {
type = AstType::INTEGER;
}
};
struct BinaryOp : Ast {
unique_ptr<Ast> lhs;
unique_ptr<Ast> rhs;
std::string op;
BinaryOp(unique_ptr<Ast>& l, unique_ptr<Ast>& r, std::string o) : lhs(std::move(l)), rhs(std::move(r)), op(o) {
type = AstType::BINARY_OP;
}
};
std::vector<Token> lex(std::string text){
std::vector<Token> tokens;
for(int i=0;i<text.size();i++){
if(text[i] == '+' | text[i] == '-'){
Token token = Token(TokenType::BINARY_OP1, std::string() + text[i]);
tokens.push_back(token);
}
if(text[i] == '*' | text[i] == '/'){
Token token = Token(TokenType::BINARY_OP2, std::string() + text[i]);
tokens.push_back(token);
}
if(text[i] == '('){
Token token = Token(TokenType::LP, std::string() + text[i]);
tokens.push_back(token);
}
if(text[i] == ')'){
Token token = Token(TokenType::RP, std::string() + text[i]);
tokens.push_back(token);
}
if(isdigit(text[i])){
std::string tmp = "";
tmp += text[i];
while(isdigit(text[i+1])){
i++;
tmp += text[i];
}
Token token = Token(TokenType::INTEGER,tmp);
tokens.push_back(token);
}
}
return tokens;
}
/*
expression ::= term1 (binary_op1 term1)*
term1 ::= term2 (binary_op2 term2)*
*/
bool consume(TokenType type, std::vector<Token>::iterator& itr){
if((*itr).type == type){
itr++;
return true;
}else{
return false;
}
}
bool lookahead(TokenType type, std::vector<Token>::iterator& itr){
if((*itr).type == type){
return true;
}else{
return false;
}
}
void expect(TokenType type, std::vector<Token>::iterator& itr){
if((*itr).type == type){
itr++;
}else{
std::cerr << "not expected" << std::endl;
std::exit(1);
}
}
unique_ptr<Ast> parseExpression(std::vector<Token>::iterator& itr);
unique_ptr<Ast> parseTerm1(std::vector<Token>::iterator& itr);
unique_ptr<Ast> parseTerm2(std::vector<Token>::iterator& itr);
unique_ptr<Ast> parse(std::vector<Token>&& tokens){
std::vector<Token>::iterator itr = tokens.begin();
return parseExpression(itr);
}
unique_ptr<Ast> parseExpression(std::vector<Token>::iterator& itr){
unique_ptr<Ast> node = parseTerm1(itr);
while(true){
std::string op = (*itr).value;
if(consume(TokenType::BINARY_OP1,itr)){
unique_ptr<Ast> rterm = parseTerm1(itr);
node.reset(new BinaryOp(node, rterm, op));
}else{
return node;
}
}
}
unique_ptr<Ast> parseTerm1(std::vector<Token>::iterator& itr){
unique_ptr<Ast> node = parseTerm2(itr);
while(true){
std::string op = (*itr).value;
if(consume(TokenType::BINARY_OP2,itr)){
unique_ptr<Ast> rterm = parseTerm2(itr);
node.reset(new BinaryOp(node, rterm, op));
}else{
return node;
}
}
}
unique_ptr<Ast> parseTerm2(std::vector<Token>::iterator& itr){
if(consume(TokenType::LP,itr)){
unique_ptr<Ast> expr = parseExpression(itr);
expect(TokenType::RP,itr);
return expr;
}
if(lookahead(TokenType::INTEGER,itr)){
int value = stoi((*itr).value);
itr++;
return unique_ptr<Ast>(new Integer(value));
}else{
std::cerr << "not expected" << std::endl;
std::exit(1);
}
}
int execute(unique_ptr<Ast> node){
if(node->type == AstType::INTEGER){
unique_ptr<Integer> i = dynamic_unique_cast<Integer,Ast>(std::move(node));
return i->value;
}else if(node->type == AstType::BINARY_OP){
unique_ptr<BinaryOp> bop = dynamic_unique_cast<BinaryOp,Ast>(std::move(node));
int a = execute(std::move(bop->lhs));
int b = execute(std::move(bop->rhs));
if(bop->op == "+"){
return a + b;
}else if(bop -> op == "-"){
return a - b;
}else if(bop -> op == "*"){
return a * b;
}else if (bop -> op == "/"){
return a / b;
}
}
return 0;
}
int main(){
std::string text;
while(true){
std::cout << ">>" << std::flush;
std::cin >> text;
if(text == "exit"){
break;
}
std::cout << execute(parse(lex(text))) << std::endl;
}
return 0;
}