Thread: Code review, Simple RPN Calculator written in C++

Page 1 of 2 12 LastLast
Results 1 to 10 of 11
  1. #1 Code review, Simple RPN Calculator written in C++ 
    Extreme Donator


    Join Date
    Jul 2009
    Age
    27
    Posts
    4,351
    Thanks given
    826
    Thanks received
    1,239
    Rep Power
    1781
    Getting into C++, figured something easy would be making a simple RPN calculator

    Anyways, any tips, etc? Here's my code:

    https://gist.github.com/tomlegodais/...6834a89d271481

    You can find my GitHub here, for what I'm currently working on.
    Reply With Quote  
     

  2. #2  
    fumant viriditas quotidiana

    saifix's Avatar
    Join Date
    Feb 2009
    Age
    30
    Posts
    1,237
    Thanks given
    275
    Thanks received
    957
    Rep Power
    3304
    Why not create a stack here and pass it to your process_* functions instead of having global state? https://gist.github.com/tomlegodais/...lc-cpp-L20-L22

    Code:
                std::vector<std::string> result = split(command);
                for (auto i = result.begin(); i != result.end(); ++i)
                          process_function(*i);
    ->

    Code:
                std::stack<double> my_stack;
                std::vector<std::string> result = split(command);
                for (auto i = result.begin(); i != result.end(); ++i)
                          my_stack.push(process_function(my_stack, *i));
    Then you can change process_operator so it doesn't need to be aware of a stack:

    Code:
    void process_operator(const std::string &op, double first, double second) {
        if (op.compare("+") == 0) {
            return first + second;
        } else if (op.compare("-") == 0) {
            return first - second;
        } else if (op.compare("/") == 0) {
            return first / second;
        } else if (op.compare("*") == 0) {
            return first * second;
        } else if (op.compare("^") == 0) {
            return pow(first, second);
        }
    }
    Code:
    double process_function(std::stack<double> &my_stack, const std::string &function) {
        double value;
    
        if (is_number(function)) {
            value = std::stod(function);
            std::cout << value << "\t" << "OPERAND" << "\t\t" << num_stack << std::endl;
        } else if (in_array(function, valid_operators)) {
            value = process_operator(function, my_stack.pop(), my_stack.pop());
            std::cout << function << "\t" << "OPERATOR" << "\t" << num_stack << std::endl;
        } else {
            // signal error if an error possible here
        }
    
        return value;
    }
    Creating a table of lambda functions would also work for operators which take 2 operands if you wanted to add a couple more functions later:

    Code:
    std::map<std::string, std::function<double(double, double)>> operator_map;
    operator_map["+"] = [](double first, double second) { return first + second; };
    edit: also needs to check for input errors
    "Im so bluezd out of my box.
    Im so fkd i canr even sens makeas Smoke blunt 420hash e tizday" - A legendary guy (1993 - 2015)
    Quote Originally Posted by nmopal View Post
    I will be creating a grimy dubstep song using these lyrics and vocaloid please prepare your bodies
    Reply With Quote  
     

  3. Thankful user:


  4. #3  
    Extreme Donator


    Join Date
    Jul 2009
    Age
    27
    Posts
    4,351
    Thanks given
    826
    Thanks received
    1,239
    Rep Power
    1781
    Quote Originally Posted by saifix View Post
    Why not create a stack here and pass it to your process_* functions instead of having global state? https://gist.github.com/tomlegodais/...lc-cpp-L20-L22

    Code:
                std::vector<std::string> result = split(command);
                for (auto i = result.begin(); i != result.end(); ++i)
                          process_function(*i);
    ->

    Code:
                std::stack<double> my_stack;
                std::vector<std::string> result = split(command);
                for (auto i = result.begin(); i != result.end(); ++i)
                          my_stack.push(process_function(my_stack, *i));
    Then you can change process_operator so it doesn't need to be aware of a stack:

    Code:
    void process_operator(const std::string &op, double first, double second) {
        if (op.compare("+") == 0) {
            return first + second;
        } else if (op.compare("-") == 0) {
            return first - second;
        } else if (op.compare("/") == 0) {
            return first / second;
        } else if (op.compare("*") == 0) {
            return first * second;
        } else if (op.compare("^") == 0) {
            return pow(first, second);
        }
    }
    Code:
    double process_function(std::stack<double> &my_stack, const std::string &function) {
        double value;
    
        if (is_number(function)) {
            value = std::stod(function);
            std::cout << value << "\t" << "OPERAND" << "\t\t" << num_stack << std::endl;
        } else if (in_array(function, valid_operators)) {
            value = process_operator(function, my_stack.pop(), my_stack.pop());
            std::cout << function << "\t" << "OPERATOR" << "\t" << num_stack << std::endl;
        } else {
            // signal error if an error possible here
        }
    
        return value;
    }
    Creating a table of lambda functions would also work for operators which take 2 operands if you wanted to add a couple more functions later:

    Code:
    std::map<std::string, std::function<double(double, double)>> operator_map;
    operator_map["+"] = [](double first, double second) { return first + second; };
    edit: also needs to check for input errors
    I like the idea of using a map for the operators, but as for the stack it's need to be global. This is simply because if we input something like

    "5 5 +" the stack will look like "[10]"

    Then we can manipulate the stack again by entering

    "3 -" resulting the stack looking like "[7]"

    You can find my GitHub here, for what I'm currently working on.
    Reply With Quote  
     

  5. #4  
    fumant viriditas quotidiana

    saifix's Avatar
    Join Date
    Feb 2009
    Age
    30
    Posts
    1,237
    Thanks given
    275
    Thanks received
    957
    Rep Power
    3304
    Quote Originally Posted by Sir Tom View Post
    I like the idea of using a map for the operators, but as for the stack it's need to be global. This is simply because if we input something like

    "5 5 +" the stack will look like "[10]"

    Then we can manipulate the stack again by entering

    "3 -" resulting the stack looking like "[7]"
    Oh right, I thought you were treating 1 line of input as 1 expression, didn't realize that was intentional. I still second making the process_operator() function unaware of the stack though, you can do that as is in process_function(). If the getline() call is just a way of buffering input and not how you're delimiting expressions then you can just process a token of input at a time. The way your code is right now is going to cause you to allocate a lot of memory for very long lines. You can avoid that by reading the next character from the input stream and deciding if you have a have a number or an operator and handling it before dealing with the next token. Coincidentally doing this you can also easily handle parsing negative numbers. It doesn't look like you're doing that at the moment.
    Last edited by saifix; 04-20-2017 at 02:26 AM. Reason: a word
    "Im so bluezd out of my box.
    Im so fkd i canr even sens makeas Smoke blunt 420hash e tizday" - A legendary guy (1993 - 2015)
    Quote Originally Posted by nmopal View Post
    I will be creating a grimy dubstep song using these lyrics and vocaloid please prepare your bodies
    Reply With Quote  
     

  6. #5  
    Extreme Donator


    Join Date
    Jul 2009
    Age
    27
    Posts
    4,351
    Thanks given
    826
    Thanks received
    1,239
    Rep Power
    1781
    Quote Originally Posted by saifix View Post
    Oh right, I thought you were treating 1 line of input as 1 expression, didn't realize that was intentional. I still second making the process_operator() function unaware of the stack though, you can do that as is in process_function(). If the getline() call is just a way of buffering input and not how you're delimiting expressions then you can just process a token of input at a time. The way your code is right now is going to cause you to allocate a lot of memory for very long lines. You can avoid that by reading the next character from the input stream and deciding if you have a have a number or an operator and handling it before dealing with the next token. Coincidentally doing this you can also easily handle parsing negative numbers. It doesn't look like you're doing that at the moment.
    Changed my code a bit:
    Code:
    #pragma once
    
    #include <iostream>
    #include <vector>
    #include <iterator>
    #include <sstream>
    #include <queue>
    #include <stack>
    #include <algorithm>
    #include <cctype>
    
    enum Step { None, Operator, Operand };
    
    bool running = true;
    std::stack<double> num_stack;
    std::vector<std::string> valid_operators = { "+", "-", "/", "*", "^" };
    Step current_step = None;
    
    std::ostream &operator<<(std::ostream &os, std::stack<double> my_stack) {
    	std::stack<double> copy_stack = my_stack;
    	int count = 0;
    
    	std::cout << "[";
    	while (!copy_stack.empty()) {
    		if (count > 0) std::cout << ", ";
    
    		os << copy_stack.top();
    		copy_stack.pop();
    
    		count++;
    	}
    
    	std::cout << "]";
    	return os;
    }
    
    std::vector<std::string> split(const std::string &text) {
    	std::vector<std::string> tokens;
    	std::istringstream iss(text);
    
    	std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(),
    		std::back_inserter(tokens));
    	return tokens;
    }
    
    bool starts_with(const std::string &text, const std::string &token) {
    	if (text.length() < token.length()) return false;
    	return text.compare(0, token.length(), token) == 0;
    }
    
    bool in_array(const std::string &value, const std::vector<std::string> &array) {
    	return std::find(array.begin(), array.end(), value) != array.end();
    }
    
    bool is_number(const std::string &str) {
    	return !str.empty() && std::find_if(str.begin(), str.end(), [](char c) { return !isdigit(c); }) == str.end();
    }
    
    void process_command(const std::string &command);
    
    double process_operator(const std::string & op, const double& secondOperand, const double& firstOperand);
    
    double process_function(const std::string &function);
    Code:
    // RPN Calculator.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "RPN Calculator.h"
    
    int main()
    {
    	while (running) {
    		std::string command;
    		std::cout << "Please enter a command/function: ";
    
    		std::getline(std::cin, command);
    		if (command.empty()) continue;
    
    		std::cout << std::endl;
    
    		if (starts_with(command, ":")) {
    			process_command(command);
    		}
    		else {
    			std::vector<std::string> result = split(command);
    			for (auto i = result.begin(); i != result.end(); ++i) {
    				double value = process_function(*i);
    				num_stack.push(value);
    
    				if (current_step == Operator) 
    					std::cout << *i << "\t" << "OPERATOR" << "\t" << num_stack << std::endl;
    				else if (current_step == Operand) 
    					std::cout << value << "\t" << "OPERAND" << "\t\t" << num_stack << std::endl;
    				
    			}
    		}
    
    		std::cout << "\t" << "RESULT" << "\t\t" << num_stack << std::endl << std::endl;
    	}
    	return 0;
    }
    
    void process_command(const std::string & command)
    {
    	if (command.compare(":q") == 0) {
    		running = false;
    	}
    }
    
    double process_operator(const std::string & op, const double& secondOperand, const double& firstOperand)
    {
    	if (op.compare("+") == 0) {
    		return (firstOperand + secondOperand);
    	}
    	else if (op.compare("-") == 0) {
    		return (firstOperand - secondOperand);
    	}
    	else if (op.compare("/") == 0) {
    		return (firstOperand / secondOperand);
    	}
    	else if (op.compare("*") == 0) {
    		return (firstOperand * secondOperand);
    	}
    	else if (op.compare("^") == 0) {
    		return pow(firstOperand, secondOperand);
    	}
    }
    
    double process_function(const std::string & function)
    {
    	double value;
    	if (is_number(function)) {
    		value = std::stod(function);
    		current_step = Operand;
    	}
    	else if (in_array(function, valid_operators)) {
    		/* Is there any stack implementation that I can get and remove in one function call? */
    		double secondOperand = num_stack.top();
    		num_stack.pop();
    
    		double firstOperand = num_stack.top();
    		num_stack.pop();
    
    		value = process_operator(function, secondOperand, firstOperand);
    		current_step = Operator;
    	}
    	return value;
    }
    Haven't implemented a map for the operators yet. Question is, is there anyway I can get and return the top of the stack without doing two method calls?

    You can find my GitHub here, for what I'm currently working on.
    Reply With Quote  
     

  7. #6  
    fumant viriditas quotidiana

    saifix's Avatar
    Join Date
    Feb 2009
    Age
    30
    Posts
    1,237
    Thanks given
    275
    Thanks received
    957
    Rep Power
    3304
    Quote Originally Posted by Sir Tom View Post
    Changed my code a bit:
    Code:
    #pragma once
    
    #include <iostream>
    #include <vector>
    #include <iterator>
    #include <sstream>
    #include <queue>
    #include <stack>
    #include <algorithm>
    #include <cctype>
    
    enum Step { None, Operator, Operand };
    
    bool running = true;
    std::stack<double> num_stack;
    std::vector<std::string> valid_operators = { "+", "-", "/", "*", "^" };
    Step current_step = None;
    
    std::ostream &operator<<(std::ostream &os, std::stack<double> my_stack) {
    	std::stack<double> copy_stack = my_stack;
    	int count = 0;
    
    	std::cout << "[";
    	while (!copy_stack.empty()) {
    		if (count > 0) std::cout << ", ";
    
    		os << copy_stack.top();
    		copy_stack.pop();
    
    		count++;
    	}
    
    	std::cout << "]";
    	return os;
    }
    
    std::vector<std::string> split(const std::string &text) {
    	std::vector<std::string> tokens;
    	std::istringstream iss(text);
    
    	std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(),
    		std::back_inserter(tokens));
    	return tokens;
    }
    
    bool starts_with(const std::string &text, const std::string &token) {
    	if (text.length() < token.length()) return false;
    	return text.compare(0, token.length(), token) == 0;
    }
    
    bool in_array(const std::string &value, const std::vector<std::string> &array) {
    	return std::find(array.begin(), array.end(), value) != array.end();
    }
    
    bool is_number(const std::string &str) {
    	return !str.empty() && std::find_if(str.begin(), str.end(), [](char c) { return !isdigit(c); }) == str.end();
    }
    
    void process_command(const std::string &command);
    
    double process_operator(const std::string & op, const double& secondOperand, const double& firstOperand);
    
    double process_function(const std::string &function);
    Code:
    // RPN Calculator.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "RPN Calculator.h"
    
    int main()
    {
    	while (running) {
    		std::string command;
    		std::cout << "Please enter a command/function: ";
    
    		std::getline(std::cin, command);
    		if (command.empty()) continue;
    
    		std::cout << std::endl;
    
    		if (starts_with(command, ":")) {
    			process_command(command);
    		}
    		else {
    			std::vector<std::string> result = split(command);
    			for (auto i = result.begin(); i != result.end(); ++i) {
    				double value = process_function(*i);
    				num_stack.push(value);
    
    				if (current_step == Operator) 
    					std::cout << *i << "\t" << "OPERATOR" << "\t" << num_stack << std::endl;
    				else if (current_step == Operand) 
    					std::cout << value << "\t" << "OPERAND" << "\t\t" << num_stack << std::endl;
    				
    			}
    		}
    
    		std::cout << "\t" << "RESULT" << "\t\t" << num_stack << std::endl << std::endl;
    	}
    	return 0;
    }
    
    void process_command(const std::string & command)
    {
    	if (command.compare(":q") == 0) {
    		running = false;
    	}
    }
    
    double process_operator(const std::string & op, const double& secondOperand, const double& firstOperand)
    {
    	if (op.compare("+") == 0) {
    		return (firstOperand + secondOperand);
    	}
    	else if (op.compare("-") == 0) {
    		return (firstOperand - secondOperand);
    	}
    	else if (op.compare("/") == 0) {
    		return (firstOperand / secondOperand);
    	}
    	else if (op.compare("*") == 0) {
    		return (firstOperand * secondOperand);
    	}
    	else if (op.compare("^") == 0) {
    		return pow(firstOperand, secondOperand);
    	}
    }
    
    double process_function(const std::string & function)
    {
    	double value;
    	if (is_number(function)) {
    		value = std::stod(function);
    		current_step = Operand;
    	}
    	else if (in_array(function, valid_operators)) {
    		/* Is there any stack implementation that I can get and remove in one function call? */
    		double secondOperand = num_stack.top();
    		num_stack.pop();
    
    		double firstOperand = num_stack.top();
    		num_stack.pop();
    
    		value = process_operator(function, secondOperand, firstOperand);
    		current_step = Operator;
    	}
    	return value;
    }
    Haven't implemented a map for the operators yet. Question is, is there anyway I can get and return the top of the stack without doing two method calls?
    No, that was a mistake in my example. You need to get a copy of the value at the top of the stack, and then the actual value itself is popped off of the stack on the pop() call (so a reference to top() after calling pop() would be invalid).

    Switching the type to an enum is a good first step, here is a full expanded example of what I meant r.e. parsing a token at a time, but it starts to get into parser territory.
    Code:
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <cctype>
    
    enum token_type {
    	TOK_INVALID = 0,
    	TOK_NUMBER,
    	TOK_OPERATOR,
    	TOK_COMMAND
    };
    
    class token {
    public:
    	token(enum token_type type, std::string val) : m_type(type), m_val(val) {}
    	
    	const enum token_type type()
    	{
    		return m_type;
    	}
    	
    	const std::string &value()
    	{
    		return m_val;
    	}
    private:
    	enum token_type m_type;
    	std::string m_val;
    };
    
    token next_token(std::istream &in)
    {
    	std::stringstream buf;
    	
    	char ch;
    	if (!in.get(ch)) {
    		// signal error;
    	}
    	
    	enum token_type type = TOK_INVALID;
    	
    	if (std::isspace(ch)) {
    		return next_token(in); // skip leading spaces
    	} else if (std::isdigit(ch)) {
    		type = TOK_NUMBER;
    	} else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^') {
    		type = TOK_OPERATOR;
    	} else if (ch == ':') {
    		type = TOK_COMMAND;
    	}
    	
    	do {
    		// additionally validate characters of tokens as you parse them
    		if (std::isspace(ch)) {
    			break;
    		}
    
    		buf << ch;
    	} while (in.get(ch));
    	
    	return token(type, buf.str());
    }
    
    int main() {
    	std::stringstream input("123456 789 +");
    
    	while (!input.eof()) {
    		token tok = next_token(input);
    
    		switch (tok.type()) {
    			case TOK_INVALID:
    				std::cout << "Invalid token" << std::endl;
    				break;
    			case TOK_NUMBER:
    				std::cout << "Number token" << std::endl;
    				break;
    			case TOK_OPERATOR:
    				std::cout << "Operator token" << std::endl;
    				break;
    			case TOK_COMMAND:
    				std::cout << "Command token" << std::endl;
    				break;
    		}
    		
    		std::cout << "Value: " << tok.value() << std::endl;
    	}
    
    	return 0;
    }
    After doing that you can switch up your main loop like this:
    Code:
    int main()
    {
            std::istream &in = std::cin;
    
    	while (running && !in.eof()) {
                    token tok = next_token(in);
                    enum token_type type = tok.type();
    
                    if (type == TOK_COMMAND) {
                       // handle your commands
                    } else if (type == TOK_NUMBER) {
                       // push number to stack
                    } else if (type == TOK_OPERATOR) {
                       // push return val to stack
                    } else {
                       // handle invalid tokens
                    }
             }
    
            std::cout << "\t" << "RESULT" << "\t\t" << num_stack << std::endl << std::endl;
    	return 0;
    }
    Though this slightly changes how your program deals with input data, since you're evaluating groups of tokens at a time at the moment. The semantics of the program remain the same though. Now you can terminate the program and print the RESULT by handling your ":q" command or sending EOF (^D in a shell). If you still want to print the result after a group of tokens, you can parse '\n' separately as a delimiter token and avoid the need for dealing with whole lines at a time.

    edit: also RPN calculators are particularly fun to play with. You can expand on this by implementing the Shunting-Yard algorithm and compiling your RPN to some target code (LLVM IR, libgccjit, bytecode), effectively implementing a small JIT compiler for math expressions.

    edit 2: on top of compiling your RPN to some target code, you could also allow calling functions defined on the target platform that you're compiling for (libc functions or java.lang.Math functions)
    "Im so bluezd out of my box.
    Im so fkd i canr even sens makeas Smoke blunt 420hash e tizday" - A legendary guy (1993 - 2015)
    Quote Originally Posted by nmopal View Post
    I will be creating a grimy dubstep song using these lyrics and vocaloid please prepare your bodies
    Reply With Quote  
     

  8. Thankful user:


  9. #7  
    Registered Member
    Anthony`'s Avatar
    Join Date
    Sep 2008
    Age
    29
    Posts
    763
    Thanks given
    75
    Thanks received
    164
    Rep Power
    204
    FYI C++11 introduced strongly-typed enums. No need to use C-style ones anymore.
    Reply With Quote  
     

  10. Thankful user:


  11. #8  
    Extreme Donator


    Join Date
    Jul 2009
    Age
    27
    Posts
    4,351
    Thanks given
    826
    Thanks received
    1,239
    Rep Power
    1781
    Quote Originally Posted by saifix View Post
    No, that was a mistake in my example. You need to get a copy of the value at the top of the stack, and then the actual value itself is popped off of the stack on the pop() call (so a reference to top() after calling pop() would be invalid).

    Switching the type to an enum is a good first step, here is a full expanded example of what I meant r.e. parsing a token at a time, but it starts to get into parser territory.
    Code:
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <cctype>
    
    enum token_type {
    	TOK_INVALID = 0,
    	TOK_NUMBER,
    	TOK_OPERATOR,
    	TOK_COMMAND
    };
    
    class token {
    public:
    	token(enum token_type type, std::string val) : m_type(type), m_val(val) {}
    	
    	const enum token_type type()
    	{
    		return m_type;
    	}
    	
    	const std::string &value()
    	{
    		return m_val;
    	}
    private:
    	enum token_type m_type;
    	std::string m_val;
    };
    
    token next_token(std::istream &in)
    {
    	std::stringstream buf;
    	
    	char ch;
    	if (!in.get(ch)) {
    		// signal error;
    	}
    	
    	enum token_type type = TOK_INVALID;
    	
    	if (std::isspace(ch)) {
    		return next_token(in); // skip leading spaces
    	} else if (std::isdigit(ch)) {
    		type = TOK_NUMBER;
    	} else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^') {
    		type = TOK_OPERATOR;
    	} else if (ch == ':') {
    		type = TOK_COMMAND;
    	}
    	
    	do {
    		// additionally validate characters of tokens as you parse them
    		if (std::isspace(ch)) {
    			break;
    		}
    
    		buf << ch;
    	} while (in.get(ch));
    	
    	return token(type, buf.str());
    }
    
    int main() {
    	std::stringstream input("123456 789 +");
    
    	while (!input.eof()) {
    		token tok = next_token(input);
    
    		switch (tok.type()) {
    			case TOK_INVALID:
    				std::cout << "Invalid token" << std::endl;
    				break;
    			case TOK_NUMBER:
    				std::cout << "Number token" << std::endl;
    				break;
    			case TOK_OPERATOR:
    				std::cout << "Operator token" << std::endl;
    				break;
    			case TOK_COMMAND:
    				std::cout << "Command token" << std::endl;
    				break;
    		}
    		
    		std::cout << "Value: " << tok.value() << std::endl;
    	}
    
    	return 0;
    }
    After doing that you can switch up your main loop like this:
    Code:
    int main()
    {
            std::istream &in = std::cin;
    
    	while (running && !in.eof()) {
                    token tok = next_token(in);
                    enum token_type type = tok.type();
    
                    if (type == TOK_COMMAND) {
                       // handle your commands
                    } else if (type == TOK_NUMBER) {
                       // push number to stack
                    } else if (type == TOK_OPERATOR) {
                       // push return val to stack
                    } else {
                       // handle invalid tokens
                    }
             }
    
            std::cout << "\t" << "RESULT" << "\t\t" << num_stack << std::endl << std::endl;
    	return 0;
    }
    Though this slightly changes how your program deals with input data, since you're evaluating groups of tokens at a time at the moment. The semantics of the program remain the same though. Now you can terminate the program and print the RESULT by handling your ":q" command or sending EOF (^D in a shell). If you still want to print the result after a group of tokens, you can parse '\n' separately as a delimiter token and avoid the need for dealing with whole lines at a time.

    edit: also RPN calculators are particularly fun to play with. You can expand on this by implementing the Shunting-Yard algorithm and compiling your RPN to some target code (LLVM IR, libgccjit, bytecode), effectively implementing a small JIT compiler for math expressions.

    edit 2: on top of compiling your RPN to some target code, you could also allow calling functions defined on the target platform that you're compiling for (libc functions or java.lang.Math functions)
    Following you advice with parsing one token at a time, here's the code implemented

    Code:
    //
    // Created by Tom LeGodais on 4/19/2017.
    //
    #pragma once
    
    #include <iostream>
    #include <sstream>
    #include <stack>
    #include <cctype>
    #include <map>
    #include <functional>
    
    enum token_type {
    	TOK_INVALID = 0,
    	TOK_NUMBER,
    	TOK_OPERATOR,
    	TOK_COMMAND
    };
    
    class token {
    public:
    	token(enum token_type type, std::string val) : m_type(type), m_val(val) {}
    
    	const enum token_type type() {
    		return m_type;
    	}
    
    	const std::string &value() {
    		return m_val;
    	}
    
    private:
    	enum token_type m_type;
    	std::string m_val;
    };
    
    token next_token(std::istream &in) {
    	std::stringstream buf;
    
    	char ch;
    	if (!in.get(ch)) {
    		throw std::runtime_error("Invalid character input!");
    	}
    
    	enum token_type type = TOK_INVALID;
    
    	if (std::isspace(ch)) {
    		return next_token(in);
    	}
    	else if (std::isdigit(ch)) {
    		type = TOK_NUMBER;
    	}
    	else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^') {
    		type = TOK_OPERATOR;
    	}
    	else if (ch == ':') {
    		type = TOK_COMMAND;
    	}
    
    	do {
    		if (std::isspace(ch)) {
    			break;
    		}
    
    		buf << ch;
    	} while (in.get(ch));
    
    	return token(type, buf.str());
    }
    
    bool running = true;
    std::stack<double> num_stack;
    
    std::ostream &operator<<(std::ostream &os, std::stack<double> my_stack) {
    	std::stack<double> copy_stack = my_stack;
    	int count = 0;
    
    	std::cout << "[";
    	while (!copy_stack.empty()) {
    		if (count > 0) std::cout << ", ";
    
    		os << copy_stack.top();
    		copy_stack.pop();
    
    		count++;
    	}
    
    	std::cout << "]";
    	return os;
    }
    
    double pop() {
    	double value = num_stack.top();
    
    	num_stack.pop();
    	return value;
    }
    
    void process_command(const std::string &command);
    
    double process_operator(const std::string &op, const double &first_op, const double &second_op);
    [code]

    Code:
    //
    // Created by Tom LeGodais on 4/19/2017.
    //
    #include "stdafx.h"
    #include <iostream>
    #include "RPN Calculator.h"
    
    int main() {
    	std::istream &in = std::cin;
    	while (running && !in.eof()) {
    		token tok = next_token(in);
    
    		enum token_type type = tok.type();
    		std::string tok_val = tok.value();
    
    		if (type == TOK_COMMAND) {
    			process_command(tok_val);
    		}
    		else if (type == TOK_NUMBER) {
    			double num_val = std::stod(tok_val);
    			num_stack.push(num_val);
    
    			std::cout << num_val << "\t" << "OPERAND" << "\t\t" << num_stack << std::endl;
    		}
    		else if (type == TOK_OPERATOR) {
    			double num_val = process_operator(tok_val, pop(), pop());
    			num_stack.push(num_val);
    
    			std::cout << tok_val << "\t" << "OPERATOR" << "\t" << num_stack << std::endl;
    		}
    		else {
    			throw std::runtime_error("Invalid token.");
    		}
    	}
    	return 0;
    }
    
    void process_command(const std::string &command) {
    	if (command.compare(":q") == 0) {
    		running = false;
    	}
    }
    
    double process_operator(const std::string &op, const double &first_op, const double &second_op) {
    	double value = 0;
    
    	if (op.compare("+") == 0) {
    		value = (first_op + second_op);
    	}
    	else if (op.compare("-") == 0) {
    		value = (first_op - second_op);
    	}
    	else if (op.compare("/") == 0) {
    		value = (first_op / second_op);
    	}
    	else if (op.compare("*") == 0) {
    		value = (first_op * second_op);
    	}
    	else if (op.compare("^") == 0) {
    		value = pow(first_op, second_op);
    	}
    
    	return value;
    }
    One question remaining, is there a way I can prompt for input? "Please enter blahaha," because as of right now with the way the code is it only asks when you input something (which isn't what we want, obviously)

    You can find my GitHub here, for what I'm currently working on.
    Reply With Quote  
     

  12. #9  
    fumant viriditas quotidiana

    saifix's Avatar
    Join Date
    Feb 2009
    Age
    30
    Posts
    1,237
    Thanks given
    275
    Thanks received
    957
    Rep Power
    3304
    Quote Originally Posted by Sir Tom View Post
    Following you advice with parsing one token at a time, here's the code implemented

    Code:
    //
    // Created by Tom LeGodais on 4/19/2017.
    //
    #pragma once
    
    #include <iostream>
    #include <sstream>
    #include <stack>
    #include <cctype>
    #include <map>
    #include <functional>
    
    enum token_type {
    	TOK_INVALID = 0,
    	TOK_NUMBER,
    	TOK_OPERATOR,
    	TOK_COMMAND
    };
    
    class token {
    public:
    	token(enum token_type type, std::string val) : m_type(type), m_val(val) {}
    
    	const enum token_type type() {
    		return m_type;
    	}
    
    	const std::string &value() {
    		return m_val;
    	}
    
    private:
    	enum token_type m_type;
    	std::string m_val;
    };
    
    token next_token(std::istream &in) {
    	std::stringstream buf;
    
    	char ch;
    	if (!in.get(ch)) {
    		throw std::runtime_error("Invalid character input!");
    	}
    
    	enum token_type type = TOK_INVALID;
    
    	if (std::isspace(ch)) {
    		return next_token(in);
    	}
    	else if (std::isdigit(ch)) {
    		type = TOK_NUMBER;
    	}
    	else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^') {
    		type = TOK_OPERATOR;
    	}
    	else if (ch == ':') {
    		type = TOK_COMMAND;
    	}
    
    	do {
    		if (std::isspace(ch)) {
    			break;
    		}
    
    		buf << ch;
    	} while (in.get(ch));
    
    	return token(type, buf.str());
    }
    
    bool running = true;
    std::stack<double> num_stack;
    
    std::ostream &operator<<(std::ostream &os, std::stack<double> my_stack) {
    	std::stack<double> copy_stack = my_stack;
    	int count = 0;
    
    	std::cout << "[";
    	while (!copy_stack.empty()) {
    		if (count > 0) std::cout << ", ";
    
    		os << copy_stack.top();
    		copy_stack.pop();
    
    		count++;
    	}
    
    	std::cout << "]";
    	return os;
    }
    
    double pop() {
    	double value = num_stack.top();
    
    	num_stack.pop();
    	return value;
    }
    
    void process_command(const std::string &command);
    
    double process_operator(const std::string &op, const double &first_op, const double &second_op);
    [code]

    Code:
    //
    // Created by Tom LeGodais on 4/19/2017.
    //
    #include "stdafx.h"
    #include <iostream>
    #include "RPN Calculator.h"
    
    int main() {
    	std::istream &in = std::cin;
    	while (running && !in.eof()) {
    		token tok = next_token(in);
    
    		enum token_type type = tok.type();
    		std::string tok_val = tok.value();
    
    		if (type == TOK_COMMAND) {
    			process_command(tok_val);
    		}
    		else if (type == TOK_NUMBER) {
    			double num_val = std::stod(tok_val);
    			num_stack.push(num_val);
    
    			std::cout << num_val << "\t" << "OPERAND" << "\t\t" << num_stack << std::endl;
    		}
    		else if (type == TOK_OPERATOR) {
    			double num_val = process_operator(tok_val, pop(), pop());
    			num_stack.push(num_val);
    
    			std::cout << tok_val << "\t" << "OPERATOR" << "\t" << num_stack << std::endl;
    		}
    		else {
    			throw std::runtime_error("Invalid token.");
    		}
    	}
    	return 0;
    }
    
    void process_command(const std::string &command) {
    	if (command.compare(":q") == 0) {
    		running = false;
    	}
    }
    
    double process_operator(const std::string &op, const double &first_op, const double &second_op) {
    	double value = 0;
    
    	if (op.compare("+") == 0) {
    		value = (first_op + second_op);
    	}
    	else if (op.compare("-") == 0) {
    		value = (first_op - second_op);
    	}
    	else if (op.compare("/") == 0) {
    		value = (first_op / second_op);
    	}
    	else if (op.compare("*") == 0) {
    		value = (first_op * second_op);
    	}
    	else if (op.compare("^") == 0) {
    		value = pow(first_op, second_op);
    	}
    
    	return value;
    }
    One question remaining, is there a way I can prompt for input? "Please enter blahaha," because as of right now with the way the code is it only asks when you input something (which isn't what we want, obviously)
    Do you mean that you'd like to prompt for input after every new line, like you were previously doing? If so, you can treat \n as a token.

    Code:
    if (ch == '\n') {
       return token(TOKEN_RETURN, ...);
    }
    ...

    Code:
    if (type == TOKEN_RETURN) {
        std::cout << "Please enter blahaha: " << std::endl;
    }
    edit: there's no reason to pass by reference for the operands here:

    Code:
    double process_operator(const std::string &op, const double &first_op, const double &second_op);
    "Im so bluezd out of my box.
    Im so fkd i canr even sens makeas Smoke blunt 420hash e tizday" - A legendary guy (1993 - 2015)
    Quote Originally Posted by nmopal View Post
    I will be creating a grimy dubstep song using these lyrics and vocaloid please prepare your bodies
    Reply With Quote  
     

  13. #10  
    Extreme Donator


    Join Date
    Jul 2009
    Age
    27
    Posts
    4,351
    Thanks given
    826
    Thanks received
    1,239
    Rep Power
    1781
    Quote Originally Posted by saifix View Post
    Do you mean that you'd like to prompt for input after every new line, like you were previously doing? If so, you can treat \n as a token.

    Code:
    if (ch == '\n') {
       return token(TOKEN_RETURN, ...);
    }
    ...

    Code:
    if (type == TOKEN_RETURN) {
        std::cout << "Please enter blahaha: " << std::endl;
    }
    edit: there's no reason to pass by reference for the operands here:

    Code:
    double process_operator(const std::string &op, const double &first_op, const double &second_op);
    Alright, I removed the referencing for the ops.

    Also, tried treating '\n' as a token, but that did not work.

    You can find my GitHub here, for what I'm currently working on.
    Reply With Quote  
     

Page 1 of 2 12 LastLast

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Proof that runescape is written in Java.
    By Hotyute in forum RS 503+ Client & Server
    Replies: 36
    Last Post: 05-31-2009, 05:55 PM
  2. How do I make a text be written in red in a HTA program??
    By k1ng 0f k1ngs in forum Application Development
    Replies: 1
    Last Post: 01-16-2009, 12:01 AM
  3. Command Maker purely written in java (beta)
    By quest rs in forum Tools
    Replies: 7
    Last Post: 03-02-2008, 01:39 PM
  4. Replies: 5
    Last Post: 02-01-2008, 12:40 AM
  5. Simple member only log in
    By Ayton in forum Tutorials
    Replies: 4
    Last Post: 01-21-2008, 07:54 AM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •