Program Language/C & C++

[C++] 계산기 만들기 (5) the driver

야곰야곰+책벌레 2022. 1. 6. 13:47
728x90
반응형

The Driver

프로그램의 모든 부분이 제자리에 있다면 드라이버만 추가하면 작업을 시작할 수 있다. 실제 계산을 수행하는 calculate()와 실제 수행을 하게 되는 main() 함수로 구성된다.

Token_stream ts {cin}; // use input from cin

void calculate()
{
	for (;;) {
		ts.get();
		if (ts.current().kind == Kind::end) break;
		if (ts.current().kind == Kind::print) continue;
		cout << expr(false) << '\n';
	}
}

int main()
{
	table["pi"] = 3.1415926535897932385; // inser t predefined names
	table["e"] = 2.7182818284590452354;
	calculate();
	return no_of_errors;
}

일반적으로 main()은 프로그램이 정상 종료되면 0을 반환하고 그렇지 않으면 0이 아닌 값을 반환한다. main() 내에는 미리 정의된 이름의 기호 테이블을 삽입한다.

calculate()의 루프는 표현식을 읽고 답을 적는 기능을 한다.

cout << expr(false) << '\n';

false 인수는 작동할 토큰을 읽기 위해 ts.get()을 호출할 필요가 없음을 expr()에 알려 준다.

Kind::end에 대한 테스트는 ts.get()이 입력 오류 또는 파일 끝을 만났을 때 루프가 올바르게 종료되는지 확인한다. Kind::print(즉, '\n' 및 ';')에 대한 테스트는 빈 표현식을 처리해야 하는 expr()의 책임을 덜어 준다.

Headers

계산기는 표준 라이브러리 기능을 사용하기 때문에 적절한 헤더 파일이 포함되어야 한다.

#include<iostream> // I/O
#include<string> // strings
#include<map> // map
#include<cctype> // isalpha(), etc

Codes

설명한 예제는 파일은 다음과 같으며, VS2015로 제작되었다.

Caculator.zip
0.01MB

// Caculator.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <map>
#include <iostream>
using namespace std;

enum class Kind : char
{
	name, number, end,
	plus = '+', minus = '-', mul = '*', div = '/', print = ';', assign='=', lp='(', rp=')'
};

struct Token
{
	Kind kind;
	string string_value;
	double number_value;
};

double expr(bool get);
double term(bool get);
double prim(bool get);

map<string, double> table;

int no_of_errors;
double error(const string& s)
{
	no_of_errors++;
	cerr << "error: " << s << '\n';
	return 1;
}

class Token_stream
{
public:
	Token_stream(istream& s) : ip{ &s }, owns{ false } {}
	Token_stream(istream* p) : ip{ p }, owns{ true } {}
	~Token_stream() { close(); }

	Token get()
	{
		//char ch = 0;
		//*ip >> ch;
		char ch;
		do {
			if (!ip->get(ch))
				return ct = { Kind::end };
		} while (ch != '\n' && isspace(ch));

		switch (ch)
		{
			case 0:
				return ct = { Kind::end };

			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;

			case ';':
			case '\n':
				return ct = { Kind::print };

			default:
				if (isalpha(ch))
				{
					ct.string_value = ch;
					while (ip->get(ch) && isalnum(ch))
						ct.string_value += ch;
					ip->putback(ch);
					ct.kind = Kind::name;
					return ct;
				}

				error("bad token");
				return ct = { Kind::print };
		}
	}
	const Token& current() { return ct; }

	void set_input(istream& s) { close(); ip = &s; owns = false; }
	void set_input(istream* p) { close(); ip = p; owns = true; }

private:
	void close() { if (owns) delete ip; }
	istream* ip;
	bool owns;
	Token ct{ Kind::end };
};

Token_stream ts{ cin };

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

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

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

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

void calculate()
{
	for (;;)
	{
		ts.get();
		if (ts.current().kind == Kind::end) break;
		if (ts.current().kind == Kind::print) continue;
		cout << expr(false) << endl;
	}
}

int main()
{
	table["pi"] = 3.1415926535897932385;
	table["e"] = 2.7182818284590452354;

	calculate();

    return 0;
}

 

728x90
반응형

'Program Language > C & C++' 카테고리의 다른 글

[C++] token 요약  (0) 2022.01.06
[C++] operator 요약  (0) 2022.01.06
[C++] 계산기 만들기 (4) error handling  (0) 2022.01.06
[C++] 계산기 만들기 (3) low-level input  (0) 2022.01.06
[C++] 계산기 만들기 (2) input  (0) 2022.01.06