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;
}


この記事が気に入ったらサポートをしてみませんか?