help on ignoring space using switch cases.

Hi,

I'm currently working on a program that deals with parsing a mathematical expression. And I'm stuck. Here is a code.

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <iostream>
#include <cstdlib>


using namespace std;

void doOperator(char ch)
{
    cout << "operator " << ch << endl;
    return;
}

void doNumber(string str)
{
    cout << "number " << atoi(str.c_str()) << endl;
    return;
}

int putTogether(string numberStack, char ch)
{
    numberStack += ch;
    ch = cin.get();
    switch (ch)
    {
        case ')':
            doNumber(numberStack);
            doOperator(ch);
            return 0;
        case '(':
            doNumber(numberStack);
            doOperator(ch);
            return 0;
        case '+':
            doNumber(numberStack);
            doOperator(ch);
            return 0;
        case '-':
            doNumber(numberStack);
            doOperator(ch);
            return 0;
        case '/':
            doNumber(numberStack);
            doOperator(ch);
            return 0;
        case '*':
            doNumber(numberStack);
            doOperator(ch);
            return 0;
        case '^':
            doNumber(numberStack);
            doOperator(ch);
            return 0;
        case '0':
            putTogether(numberStack, ch);
            return 0;
        case '1':
            putTogether(numberStack, ch);
            return 0;
        case '2':
            putTogether(numberStack, ch);
            return 0;
        case '3':
            putTogether(numberStack, ch);
            return 0;
        case '4':
            putTogether(numberStack, ch);
            return 0;
        case '5':
            putTogether(numberStack, ch);
            return 0;
        case '6':
            putTogether(numberStack, ch);
            return 0;
        case '7':
            putTogether(numberStack, ch);
            return 0;
        case '8':
            putTogether(numberStack, ch);
            return 0;
        case '9':
            putTogether(numberStack, ch);
            return 0;
        case '.':
            putTogether(numberStack, ch);
            return 0;
        case ' ':
        default:
            doNumber(numberStack);
    }
    return 0;
}


int main()
{

    char ch;
    ch = cin.get();
    switch (ch)
    {

        case ')':
            doOperator(ch);
            break;
        case '(':
            doOperator(ch);
            break;
        case '+':
            doOperator(ch);
            break;
        case '-':
            doOperator(ch);
            break;
        case '/':
            doOperator(ch);
            break;
        case '*':
            doOperator(ch);
            break;
        case '^':
            doOperator(ch);
            break;
        case '0':
            putTogether("",ch);
            break;
        case '1':
            putTogether("",ch);
            break;
        case '2':
            putTogether("",ch);
            break;
        case '3':
            putTogether("",ch);
            break;
        case '4':
            putTogether("",ch);
            break;
        case '5':
            putTogether("",ch);
            break;
        case '6':
            putTogether("",ch);
            break;
        case '7':
            putTogether("",ch);
            break;
        case '8':
            putTogether("",ch);
            break;
        case '9':
            putTogether("",ch);
            break;
        case '.':
            putTogether("",ch);
            break;
        case ' ':// this should ignore a space?
        default:
            return 0;
    }
    return 0;
}


The program is suppose to parse the expression.
So if my input was
30 + 3.2 * 4.5

the output should be
number 30
oprator +
number 3.2
oprator *
number 4.5

But I code above can only read the first chunk before the first space
So I'm thinking it is not ignoring the spaces like it should do.

I appreciate any kind of help, thanks in advance.
You have an interesting start, but I wouldn't do it like that. For example there is a function called isdigit(char nChar) for digits and there is a function called isspace(char nChar) of the <cctype> which I could use this way.

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
#include <iostream>
#include <cctype>
#include <string>
#include <cstdlib>

using namespace std;

int main()
{
     string strNumber;
     int numberValue;
     string strInput;
     string strOperatior; // assuming I have a single operator.
     int nIndex=0;
     char nChar;
     bool bNumberDone = false;

     cout << "Please enter mathematical expression:" << endl;
     cin >> noskipws;  // so I can catch the white space after the last number.
     getline(cin, strInput);

     while(nIndex < strInput.length())
     {
            bNumberDone = false;
            nChar = strInput[nIndex];
            if(!isspace(nChar))
            {
                   if(isdigit(nChar))
                   {
                         strNumber+=nChar;
                   }
                   else
                   {
                         //  this is where I would parse for all the fun characters.
                         bNumberDone = true;
                         strOperator+=nChar;
                  }
             }
             else
             {
                   bNumberDone = true;
             }

             if(bNumberDone)
             {
                   if(!strNumber.empty())
                   {
                         cout << "Number: " << strNumber << endl;
                         nNumberValue = atoi(strNumber.c_str());
                         strNumber.clear(); // clear the string.

                         // move the number onto a stack if need be.
                         // deal with the current operator or put on the stack as well depending which way I store things.
                   }
                 
                   if(!strOperator.empty())
                   {
                         // deal with the operator....
                   }
             }
             nIndex++;
       }// while...length
       return 0
}


This code represents how we do parsing usually. Note there are not switches in it, that is used after the parse phase, which I didn't represent here. I don't know if you know what a vector is, or other data containers of c++ to expand this any further.
Last edited on
Thanks Azagaros.

I see your point. Yet, I am not very good at using vectors...

Is there any way I could do to improve my code?
Hi zakels

The problem in you original code is not that you're ignoring the spaces in your switch statement. Its because you main function exits after it processes the first char it gets.

Adding code to report all gets, all scope entrances and exits, and when spaces are found, the output I get for "30 + 3.2 * 4.5" is:

[begin main]
30 + 3.2 * 4.5
main got 3
[begin putTogether]
putTogether got 0
[begin putTogether]
putTogether got
putTogether found a space : fall through to default
[begin doNumber]
number 30
[end doNumber]
[end putTogether]
[end putTogether]
[end main]


Note that the space case in main is not triggered.

Various comments on the original code:

1. return; is optional as the last statement in a function with a void return type. Most people would omit it.

2. multiple returns from a function should be generally avoided. It made it harder than necessary for me to add debug logging. Well, it was just a search and replace in the end, but I ended up with a log statement for each case statement whereas I should have just needed one at the end. Here it's better to use break; rather than return 0; for the case statements. e.g.

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
int putTogether(string numberStack, char ch)
{
#ifdef LOGGING
    cout << "[begin putTogether]" << endl;
#endif
    numberStack += ch;
    ch = cin.get();
    switch (ch)
    {
        case ')':
            doNumber(numberStack);
            doOperator(ch);
            break;
        case '(':
            doNumber(numberStack);
            doOperator(ch);
            break;
// <snip>
        case '0':
            putTogether(numberStack, ch);
            break;
        case '1':
            putTogether(numberStack, ch);
            break;
// <snip>
        case '.':
            putTogether(numberStack, ch);
            break;
        case ' ':
        default:
            doNumber(numberStack);
    }
#ifdef LOGGING
    cout << "[end putTogether]" << endl;
#endif
    return 0;
}


3. if multiple cases use use the same code, it is ok to merge them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    switch (ch)
    {
        case ')':
        case '(':
        case '+':
// <snip>
        case '^':
            doNumber(numberStack);
            doOperator(ch);
            break;
        case '0':
        case '1':
// <snip>
        case '9':
        case '.':
            doNumber(numberStack);
            break;
        case ' ':
            cout << "putTogether found a space : fall through to default" << endl;
        default:
            doNumber(numberStack);
    }


But this suggests the same move to me as Azagaros suggests: to uses isdigit(). I suggest you define your own isoperator() function to make things consistent and tidy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    if(isoperator(ch))
    {
            doNumber(numberStack);
            doOperator(ch);
    }
    else if(isdigit(ch) || (ch == '.'))
    {
            doNumber(numberStack);
    }
    else if(isspace(ch))
    {
            doNumber(numberStack); // not sure about this?
    }
    else
    {
            // handle error here!
    }


Finally, you should note how your main switch statement and the one in putTogether are kind of similar. Ideally should should have just the one.

And you will need to track more state than the bNumberDone variable Azagaros' code uses, if you want to handle operator precedence and brackets.

Most exercise books get you to solve the postfix operator case first, and then the infix case. As is the case for your example, 30 + 3.2 * 4.5, the + operator cannot be applied until after the * operator. So you will need a way to store values and operators until you need to apply them.
Last edited on
in my original code I would follow it like this for the period or other operators.
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

            bNumberDone = false;
            nChar = strInput[nIndex];
            if(!isspace(nChar))
            {
                   if(isdigit(nChar))
                   {
                         strNumber+=nChar;
                   }
                   else
                   {
                         if(nChar == '.')
                         {
                                strNumber+=nChar;
                         }
                         else
                         {
                         //  this is where I would parse for all the fun characters.
                               bNumberDone = true;
                               strOperator+=nChar;
                         }
                  }
             }
             else
             {
                   bNumberDone = true;
             }


Managing the other operators I would do in this section. I wouldn't need to define am IsOperator() function. I would send the operator to a function to see what it is and deal with the stack in a manageable way based on the operator. This is where the switch statement would be used. In the default section would have the error catch if it was set up.

for example:
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
bool DoOperator(const std::string &strOperator)
{
       bool bSuccess = true; // assume true for our success.
       char nChar = strOperator[0]; // assuming we have a single character operator.
        
        switch(nChar)
        {
               case '(':
                      // we have a open grouping symbol to deal with, track depth.
                      break;
               case ')':
                      // we have a closing grouping symbol to deal with, track depth
                      break;
               case '*':
               case '+':
               case '-': 
               case '/':
                      // we have a math valid math operation deal with
                      // there may be some precedence we need to deal with.
                     break;
                default:
                       cout<< "Unknown: " << strOperator << endl;
                       bSuccess = false;
                      break;
          }
          return bSucces;

  }// end function. 
Last edited on
Topic archived. No new replies allowed.