Getting variables from and calculate on it c++

Hi,

I need to develop a calculator which reads the input as shown below from a text file and calculate the expression given by the user.

Input:

i = 1
j = 2
i + j * 3

Output should be: 7

Can somebody please help me?
This is not as simple as it looks.

First, you have to read the user's input.

Next, you need to parse the input and look for
- variable names
- assignment to variables
- expressions

You need to keep a map of variable names to current values

When you reach an expression, you need to put it into an evaluation tree (or on a stack in reverse Polish notation).

Finally, you need to perform the evaluation of the expression and print out the answer.

p.s. I did part of this in a college class decades ago. I'm not going to go at it again right now.
Yeah i am sure its not that simple and i have tried to use map but its still not working.
open file, read it line by line ... i guess you know how to do that
(if not, take a look at http://www.cplusplus.com/reference/istream/istream/getline/)

if a line contains a '=', add a new entry to a map:

std::map<std::string, int> variables;

for example, when you parse line 1, which is "i = 1", you do:

variables[new_var] = new_value;

new_var would be "i"
new_value would be "1"

------------------------------------------

otherwise, if you get a statement that CAN be evaluated, for example:

i + j * 3

check if you have i and j stored in "variables", if so, then calculate it according to the rules of math (* before + etc). you can also try to figure out what has to be evaluated first, for example:

i + j * 3

gets interpreted as:

term1 + term2 (according to the rule stated above, * before +)

you have to recursively break down the line in values and operations first.

std::function<...> can store an operation (for example, the "+" and another "*")

good luck ^^
Last edited on
I am new to c++ so @John87Connor could you please include some code please!
I am new to c++ ...
Yeah i am sure its not that simple ...


Maybe you can explain a little better what you are trying to do or why you are trying to accomplish this.

Is this a school assignment? If so, your teacher should be able to give you some pointers as to how to set up the evaluation structures.

If this is a project that you came up with on your own, then this is a bit ambitious for a C++ newbie.

I recommend the tutorials on this site (http://www.cplusplus.com/doc/tutorial/) for learning the basics of C++. The reference pages on this site are also fantastic.

Instead of the page mentioned in @John87Connor's post (type it in--the link got messed up with extra characters), I would recommend http://www.cplusplus.com/reference/string/string/getline/ because this function works with strings rather than char[].

This page ( http://www.cplusplus.com/reference/string/string/find_first_of/ ) shows how to find specific characters (such as '=') in a given string.

Example code would be:

1
2
3
4
5
6
7
8
9
10
11
12
13
  std::string line;

  std::getline (std::cin, line);

  size_t equalSign = line.find_first_of('=');
  if (equalSign == string::npos)
  {
    // No equal sign found
  }
  else
  {
    // Equal sign found at index equalSign
  }


I hesitate to give you more than this because I suspect the project you are attempting is way beyond your experience level. This is not a "please give me a hint" type of problem. If you are truly a new programmer, you will probably need dozens upon dozens of little hints to get near the solution you are looking for. For instance, @John87Connor's comment
... calculate it according to the rules of math (* before + etc).
is an extremely complicated thing to do for a beginner, which is why you need some sort of well-understood data structure (maybe tree or stack) and algorithm. This complexity is not apparent from a simple line in a forum post. And this doesn't even address handling errors when parsing the input, which could be 2/3 or more of the total parsing code.

Please, if you can, start with something smaller and much more manageable. When you have built up your coding experience and understand more of the language, then you will be able to tackle some of the algorithm stuff required by the expression evaluator you are now trying to write.
lets start here. you can add file reading to it after you understand this much.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
int f(int i, int j)  //the function you want to compute. using math f(x) naming here for simple example
{
  return i + j*3;
}

int main()
{
    int i,j;
    std::cout<< "enter i\n";
    std::cin >> i;
    std::cout<< "enter j\n";
    std::cin >> j;
    std::cout << f(i,j);
}


this isnt even close to what you need, but you said you were brand new...
expression parsing is complicated and you should do some basics first.
Last edited on
If i*j=3 can be any valid mathematical expression, then you need to parse it.
You can take inspiration from here: http://www.codinghelmet.com/exercises/expression-evaluator

If the first two lines can also be any assignment along any variable' name then again things will be more complex then they seem. You need to both evaluate expression and keep track of LHS for future expressions.

(I'm interested in doing this, haven't done it so far so maybe I'll share my code with you when it's done.. just that I don't have so much time right now)

If you're relatively new to C++ or you don't know about pointers yet then ignore all of that above.
You need to use menus. Basically jonnin's code but with menus if * can be other mathematical operations.

-> Ask value of first variable and second variable
-> Ask which operation to do
-> Run function for that operation and output
Last edited on
if your college has a library look for practices & principles by bjarne stroustrup

he explains how to do this in detail early on in the book,it's solved by breaking the digits into tokens then a grammar is created using recursion

it is quite a tricky topic to be honest.
So I may aswell explain while it's relatively fresh in my head(been about 7-8 months since I read Bjarnes book) but I glanced over the code I wrote back then, I'll break it down for you.

So obviously you know multiplication and division will be done before addition and subtraction but the compiler does not know that this rule holds,so you have to come up with a way to make sure the the right order of operations are carried out or else your answer will be wrong but again the compiler doesn't know this. So you need to implement a grammar,a grammar is just a set of rules for example there is grammars in languages - https://www.skillsyouneed.com/write/grammar1.html, I'm not an English professor so I won't explain it in detail but read that link.

Now you want to implement your own grammar,you want to tell the compiler to multiply and divide before you subtract and add. This is quite tricky but we can accomplish this using while loops and recursion but first we need a way for our grammar to be parsed,so lets break our input into tokens(Tokenizing). Our token will have a type and a number,if the type is a sign such as '+' we will assign it the number 0(this number isn't going to be used anyway) if we have a sign we will assign a fixed random number to it lets use '8', why 8? no reason at all we could have picked any number but Bjarne seemed to like 8. This Token class is fairly simple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Token
{

public:
    double number;
    char type;

    Token()
    {
        type = 'f';
        number = 0;
    }

    Token(int number,char type)
        : number(number),type(type)
    {

    }
    Token(char t)
    {
        type = t;
        number = 0;
    }
};


Next we need a way of storing our tokens,for this we will create a token stream class this token stream class will behave a lot like cin( but we won't be overloading the insertion or puts operator << >> , for simplicity ). This token stream class will also be like a wrapper of cin,and the class will be mostly using the functionality of cin except we will be converting to tokens depending on what input we get from cin. Just like cin we will have a putback function,that will put a token back into our stream if it was not used,we will need a buffer to hold this and also a boolean value to represent if the buffer is full or if it's empty.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class TokenStream
{

public:
    Token buffer;
    bool bufferFull;

    TokenStream()
    {
        buffer = NULL;
        bufferFull = false;

    }

    Token getToken()
    {
        if(bufferFull)
        {
            bufferFull = false;
            return buffer;
        }

        char value;

        cin >> value;

        switch(value)
        {


        case '+':
        case '*':
        case '/':
        case 'q':
        {
            Token t(value);
            return t;
        }
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        {
            cin.putback(value);
            double num;
            cin >> num;
            Token t(num,'8');
            return t;
        }

        }
    }

    void putBack(Token t){

       if(bufferFull){

          return;
       }
       bufferFull = true;
       buffer = t;
    }
};


Now that we have our Token and Token stream class ready to go all we need to do is set up our grammar which is by far the hardest part of the problem.Lets break this down into terms and expressions and primaries,an expression is for example 5 + 5 * 2 or 6 - 2,a term is 5*2 or 5(2),-2, and a primary in this case will just return the number we want to work with(note Bjarne has a much better explanation in his book) so what is our grammar? well a term is made up of primaries lets say 5 * 2 or 5(2),the primaries being 5 and 2 and an expression is made up of terms so 5 + 5 * 2, ie we want the terms to be evaluated before the expression. We make use of iteration and and recursion,(recursion is just a function calling itself)

Ok so lets start with the expression function first


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28


double expression(){

      double left = term();

      Token t = ts.getToken();

      while(true){

        switch(t.type){

      case '+':

        left+= term();
        t = ts.getToken();
        break;
      case '-':
        left-= term();
        t = ts.getToken();
        break;
      default:
          ts.putBack(t);
        return left;

        }
      }
}


our value on the left is a term,which is a primary(which will be shown in the term function) so lets call term() and set it's value to whatever term returns.I will come back to the rest of the code,but now we jump into the term() function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
double term(){

    double left = primary();

    Token t = ts.getToken();

    while(true){

      switch(t.type){

  case '*':
       left *= primary();
       t = ts.getToken();
       break;
  case '/':
       left /= primary();
       t = ts.getToken();
       break;
  default:
    ts.putBack(t);
    return left;

      }
    }
}


the left value of our term will be a primary,so we call primary which returns a number,

1
2
3
4
5
6
7
8
9
10
11
12
13
double primary(){

    Token t = ts.getToken();

    switch(t.type){

  case '8':
     return t.number;
  default:
     ts.putBack(t);
     return 0;
    }
}


we create a Token named t and use our token stream getToken() function to get a token if done right this should return a number we then check this in a switch if t.type equals '8' we know it's a digit so we return t.number.

Now we come back to our term() function, left is now equals the number returned from primary,we then create another Token t and get a token from our token stream,this token could be a '+' or a '*' but it should be a sign,so now we enter a while loop and evaluate the sign if it's a '*' we call primary and *= it to value of left,if it's not a '*' or '/' we break from the while loop and put back our token to this token can be used by calling expression function. Lets stick with it being a '*' so now after we have done the *= on left we get another Token from the stream and set it equal to t,again this token should be a sign,if a '*' comes up again we repeat the step above again and again until we get a character that is not a '*' or '/'.

lets say we have no more '*'s now we return the left from this function,we now return back to expression and left is equal to the number returned from term(),now get the next Token and enter the while loop in expression(), we check if the sign is a '+' or a '-',so we call term again if there is no more multiplication or division to be done term returns a a number which is returned from primary() now this number is +='d or -='d to left and left is returned

we also need to have a quit condition so we will check to see if it's met in our calculate function,which calls expression,result will hold the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

void calculation(){

  Token t;
    int result = 0;

     while(true){

        cout << "enter expression" << endl;
        t = ts.getToken();

        if(t.type == 'q'){

            break;
        }
        ts.putBack(t);

        result = expression();
     }

     cout << "result :: " << result << endl;
}

int main()
{

   calculation();
}


This is quite a lot to take in and understand but after you go through the code a couple of times it should start to make sense, again if you want a better explanation read Bjarnes book he spends almost a full chapter explaining this.

if you have any questions I'll try and answer them.

Good luck
Last edited on
Topic archived. No new replies allowed.