[ create a new paste ] login | about

Link: http://codepad.org/RFm9DKzb    [ raw code | fork ]

Cyn - C++, pasted on Jun 25:
//  Calculator.cpp

#include "Calculator.h"

//------------------------------------------------------------------------------

const char let = '#';
const char quit = 'Q';
const char print = ';';
const char number = '8';
const char name = 'a'; // variable var_table
const char square_root = 'r'; 
const char power = 'p'; 
const char constant = 'c';
const char help = 'H';

//------------------------------------------------------------------------------
 
Token Token_stream::get() 
{
	if (full) { full=false; return buffer; }
	char ch;
	inputstream.get(ch);

	if (!inputstream) return Token(quit); // if EOF -  quit; without this string code will crash

	if(isspace(ch)) { // handle with spaces
		if (ch == '\n') return Token(print);
		inputstream>>ch;
	}
	
	switch (ch) {
	case print:
	case '(':
	case ')':
	case '+':
	case '-':
	case '*':
	case '/':
	case '%':
	case '=': // not used yet
	case ',': // comma used to separete arguments in pow(argument_X,argument_Y);
	case '#': // new "let"
		return Token(ch);
	case '.':
	case '0': 	case '1': 	case '2': 	case '3':	case '4':
	case '5':	case '6':	case '7': 	case '8': 	case '9':
	{	inputstream.unget();
		double val;
		inputstream >> val;
		return Token(number,val);
	}
	default:					// used to define var_table
		if (isalpha(ch)) {		// returns true if ch is a letter, isdigit(ch) - return true if ch is a digit
			string s;
			s += ch;
			while(inputstream.get(ch) && (isalpha(ch) || isdigit(ch) || ch=='_') ) s += ch;
			inputstream.unget();
 			if (s == "let") return Token(let);		// "let" name "=" expression
			if (s == "const") return Token(constant);
			if (s == "exit" || s == "quit") return Token(quit);
			if (s == "sqrt") return Token(square_root);
			if (s == "pow") return Token(power);
			if (s == "help" || s == "h" || s == "H" || s == "Help") return Token(help);
			return Token(name,s);
		}
		
		error("Bad token");
	}
}

//------------------------------------------------------------------------------
 
void Token_stream::ignore(char c)	// provides performance cycle of calculating, through skipping wrong inputted symbols, used in the catch-block; c = ';' (print)
{									// handle with set of wrong and right statements, for input: "wrong_statement; right_statement; wrong_statement;" output will be:
	if (full && c==buffer.kind) {   // "error; result; error " (if right_statement is not a declaration );
		full = false;
		return;
	}
 
	full = false;
 
	char ch;
	while (inputstream.get(ch))
		if (ch==c || isspace(ch)) return;
 
}

//------------------------------------------------------------------------------

Table Symbol_table;

//------------------------------------------------------------------------------

double Table::get_value(string s)
{
	for (int i = 0; i<var_table.size(); ++i)
		if (var_table[i].name == s) return var_table[i].value;
	error("get: undefined name ",s);
}
 
//------------------------------------------------------------------------------

void Table::set_value(string s, double d) 
{
	for (int i = 0; i<var_table.size(); ++i)
		if (var_table[i].name == s && !var_table[i].constant) { 
					var_table[i].value = d;
				return;
		}
		else
			if (var_table[i].name == s && var_table[i].constant)
				error("set: attempt to redefine a constant");
 
	error("set: undefined name ",s);
}
 
//------------------------------------------------------------------------------

bool Table::is_declared(string s)
{
	for (int i = 0; i<var_table.size(); ++i)
		if (var_table[i].name == s) return true;
	return false;
}

//------------------------------------------------------------------------------

double Table::declare(Token_stream& ts) // declares a variable; new grammatics unit declaration is "let" var_name " = " expression;
{
	bool is_constant = false; 
	Token t = ts.get();
	if (t.kind == constant)
		is_constant = true;
	else
		ts.unget(t);
	t = ts.get();
	if (t.kind != name) error ("name expected in declaration");
	string name = t.name;
	if (is_declared(name)) error(name, " declared twice");
	Token t2 = ts.get();
	if (t2.kind != '=') error("= missing in declaration of " ,name);
	double d = expression(ts);
	var_table.push_back(Variable(name,d,is_constant));
	return d;
}

//------------------------------------------------------------------------------

double x_power_y(Token_stream& ts)
{
	Token t = ts.get();
	if (t.kind != '(') error("x_power_y: '(' expected");
	double number = expression(ts);
	t = ts.get();
	if (t.kind != ',') error("x_power_y:',' exptected.");
	double power = expression(ts);
	t = ts.get();
	if (t.kind != ')') error("x_power_y:')' expected");
	int pow = power;
	if (pow != power) error("x_power_y: NON-integer power!");
	if (pow < 0) error ("x_power_y: Negative power!");
	if (pow == 0) return 1;
	double multiplier = number;
	for (int i = 1; i < pow; i++)
	number *= multiplier;
	return number;
}
 
//------------------------------------------------------------------------------

double primary(Token_stream& ts)
{
	Token t = ts.get();
	switch (t.kind) {
	case '(':
	{	double d = expression(ts);
		t = ts.get();
		if (t.kind != ')') error("')' expected");
		Token next = ts.get();
		if (next.kind == number)
			d *= next.value;
		else
			ts.unget(next);
		return d;
	}
	case '-':
		return - primary(ts);  // handle "-2+3++3;" input 
	case '+':
		return primary(ts);	
	case number:			// handle multipliers for names, e.g input: #x = 3; 3x; output: =3; =9;
		{
		Token next = ts.get();
		if(next.kind == name)	
			return t.value * Symbol_table.get_value(next.name);
		else
			ts.unget(next);
		return t.value;
		}
	case name: // access to variable by its name
		{
			double d = Symbol_table.get_value(t.name);
			string lvalue = t.name;		
			t = ts.get();
			if (t.kind == '=') { // if assigment operator goes after name
				double rvalue = expression(ts);
				Symbol_table.set_value(lvalue, rvalue);
				return Symbol_table.get_value(lvalue);
			}
			else 
				ts.unget(t);
			return d;
			break;
		}
	case square_root:
		{
			double d = primary(ts);
			if (d>=0) return sqrt(d);
			error("Can't calculate square root of a negative number.");
			break;
		}
	case power:				// pow(number,power)
			return x_power_y(ts);
			break;	
	case help:
		calc_help(ts.get_ostream());
		break;
	default:
		error("primary expected");
	}
}
 
//------------------------------------------------------------------------------
double term(Token_stream& ts)
{
	
double left = primary(ts); // 
	while(true) {
		Token t = ts.get();
		switch(t.kind) {
		case '*':
			left *= primary(ts);
			break;
		case '/':
		{	double d = primary(ts);
			if (d == 0) error("divide by zero");
			left /= d;
			break;
		}
		case '%':
		{
			double right = primary(ts);
			int i1 = left;
			int i2 = right;
			if (i1 != left) error("Left operand of % isn't an integer number");
			if (i2 != right) error("Right operand of % isn't an integer number");
			if (i2 == 0) error("Deviding by zero.");
			left = i1%i2;
			break;
		}
		case '(': // handle missed '*', e.g. x(x-y)(x+y)
		case name:
		case square_root:
		case power:
			ts.unget(t);
			left *= primary(ts);
			break;
		default:
			ts.unget(t);
			return left;
		}
	}
}
//------------------------------------------------------------------------------
 
double expression(Token_stream& ts)
{
	double left =  term(ts);  
	while(true) {
		Token t = ts.get();
		switch(t.kind) {
		case '+':
			left += term(ts);
			break;
		case '-':
			left -= term(ts);
			break;
		default:
			ts.unget(t);
			return left;
		}
	}
}
//------------------------------------------------------------------------------

void predefine() // define constants
{
	Variable kilo("k",1000.0,true);
	Symbol_table.pushback(kilo);
}

//------------------------------------------------------------------------------


double declaration(Token_stream& ts)
{
	return Symbol_table.declare(ts);
}

//------------------------------------------------------------------------------


double statement(Token_stream& ts) // new grammatics unit, that forking to declarations (of variables) or expressions; statement is declaration or expression;
{
	Token t = ts.get();
	switch(t.kind) {
	case let:
		return declaration(ts);
	default:
		ts.unget(t);
		return  expression(ts);

	}
}

//------------------------------------------------------------------------------

 
void clean_up_mess(Token_stream& ts) // handle with mistakes, proceeding permanent perfomance
{
	ts.ignore(print);
}

//------------------------------------------------------------------------------

void calc_help(ostream& ost)
{
	ost << "\n*HELP*\n";

	ost << "This calculator supports:\n\n"
			" - ordinary arithmetic operations and dividing by mod, thats usage is x%y;\n"
			"where x and y are integer numbers.\n"
			"Exaple of input:\n15/(7-5+1)%2+9\noutput:\t= 10\n\n"
			" - power computation, thats usage is pow(x,y), where x is a number\n"
			"and y - power, y - must be an integer number.\n"
			"Exaple of input:\npow(3.3,4)\noutput:\t= 118.592\n\n"
			" - square root computation, thats usage is sqrt(x), where x >= 0\n"
			"Exaple of input:\nsqrt(10.1)\noutput:\t= 3.1785\n\n"
			" - variables.\nTo define a variable use word let or '#', followed by name\n"
			"of variable, assigment operator and value or expression, e.g:\n"
			"input: let var_1 = 100; #var_2 = var_1-10;\n"
			"\nVariable name must begin with a character and may contain characters,\n"
			"digits and symbol of underscore '_'\n"
			"To reassign variable use assign operator '=', e.g:\n"
			"let x = 1; let y = x; x = 9; y = 1/x\n"
			"\nTo declare a constant use \"let const variable_name = value\", e.g:\n"
			"let const pi = 3.14\n"
			"\nNotice that attempt to reassign a constant will return error.\n"
			"\nPriority of operators:\n\t- parentheses '(' ')' have highest priority;"
			"\n\t- '*', '/', '%', sqrt(x), pow(x,y) have equal middle priority;"
			"\n\t- '+' and '-' have equal lowest priority.\n"
			"\nTo calculate expressions press Enter or input ';'.\n"
			"\nType 'q', 'quit' or 'exit' to exit and 'h','help', 'H' or 'Help' to see \n"
			"this help documentation.\n";
}

//------------------------------------------------------------------------------

const string prompt = "> ";

//------------------------------------------------------------------------------

const string result = "= ";

//------------------------------------------------------------------------------
 
void calculate(istream& ist, ostream& ost)   // cycle of calculating with errors processing
{
	predefine();
	Token_stream ts(ist,ost);
	ost << "Use 'help' for instructions of input.\n";
	while(true)
		try {
		ost << prompt;
		Token t = ts.get();
		while (t.kind == print) t=ts.get();
		if (t.kind == quit) return;
		ts.unget(t);
		double d = statement(ts);
		if (d == d) // checking of NaN (its cause when calc_help() called from primary)
			ost << result << d << endl; // << fixed << setprecision(4) 
	}
	catch(runtime_error& e) {
		ost << e.what() << "\nEnter to proceed." << endl;
		clean_up_mess(ts);
	}
}


Create a new paste based on this one


Comments: