string calculator

i need to write a calc,

can someone tell me how to check for braces and calc between? and few more steps how to write it?



Last edited on
I need to write a calc, example S="45+(2-33*8)*2-4*9" (i can use recursion)

Yeah, how is this a recursion?
I'd recommend you read Programming: Principles and Practice Using C++ by Stroustrup. There are 2 chapters (Ch 6 - 7) dedicated to creating a calculator as you mentioned.

Here's my summary(may not be perfect):
Basically, you would have to define a grammar(a set of rules) which tells C++ how to parse expressions. For example, this is the grammar Stroustrup defined for a calculator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
Expression:
    Term
    Expression '+' Term
    Expression '-'

Term:
    Primary
    Term '*' Primary
    Term '/' Primary

Primary:
    Number
    '(' Expression ')'

Number:
    floating point literal
*/

An expression can consist of:
-> a term
-> itself + a term
-> itself - a term

For example, is 45 + 27 an expression?
We read the above sequentially and break each section into tokens(a unit that represents something). In this case, the tokens would be 45, +, and 27. Then we read each token and apply the above grammar to it.
45 is a number, which is a primary, which is a term, which is an expression.
The next character we see is a '+', so we must look for a term after that, so that it satisfies the "Expression + Term" grammar.
27 is a number, which is a primary, which is a term.
Thus, 45 + 27 satisfies the "Expression + Term" grammar, so it is an expression.

Now to implement this grammar into 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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <cctype>
#include <iostream>
using namespace std;

const char number_t{ 'n' }, print_ch{ '\n' }, quit_ch{ 'q' };

/*
type: Is it a number, operator or parentheses?
value: If it's a number, what is its value?
*/
class Token
{
public:
    Token( ) : type{}, value{}
    {

    }

    // we'll only use the type member variable
    // to store parentheses and operators
    Token( char t ) : type{ t }, value{}
    {

    }

    // for numbers
    Token( double v ) : type{ number_t }, value{ v }
    {

    }

    char type;
    double value;
};

/*
a wrapper for cin
to read Tokens from input
*/
class Token_stream
{
public:
    Token_stream( ) : token{}, is_full{}
    {

    }

    Token get( );

    // for when we don't use the Token
    // easier to demonstrate with an example
    void putback( const Token& t );

private:
    Token token;
    bool is_full;
};

Token Token_stream::get( )
{
    if( is_full ) {
        is_full = false;
        return token;
    }

    char c{};
    // '\n' returns true for isspace
    while( cin.get( c ) && isspace( c ) )
        if( c == print_ch ) return Token{ print_ch };

    switch( c )
    {
    case '+': case '-': case '*': case '/':
    case '(': case ')':
    case print_ch: case quit_ch:
        return Token{ c };

    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
    case '.':
    {
        cin.putback( c );
        double d{};
        cin >> d;

        return Token{ d };
    }

    default:
        throw "bad token";
    }
}

void Token_stream::putback( const Token& t )
{
    if( is_full ) throw "putback when full";

    token = t; is_full = true;
}

double expression( Token_stream& ts );

// deals with numbers and parentheses
double primary( Token_stream& ts )
{
    Token t{ ts.get( ) };

    switch( t.type )
    {
    case number_t:
        return t.value;

    case '(':
    {
        t = expression( ts );
        Token t2{ ts.get( ) };
        if( t2.type != ')' ) throw "expected ')'";
        return t.value;
    }

    default:
        throw "primary expected";
    }
}

// deals with * and /
double term( Token_stream& ts )
{
    double left{ primary( ts ) };

    while( true ) {
        Token t{ ts.get( ) };

        switch( t.type )
        {
        case '*':
            left *= primary( ts );
            break;

        case '/':
        {
            double right{ primary( ts ) };
            if( right == 0.0 ) throw "divide by 0";
            left /= right;
            break;
        }

        default:
            ts.putback( t );
            return left;
        }
    }
}

// deals with + and -
double expression( Token_stream& ts )
{
    double left{ term( ts ) };

    while( true ) {
        Token t{ ts.get( ) };

        switch( t.type )
        {
        case '+':
            left += term( ts );
            break;

        case '-':
            left -= term( ts );
            break;

        default:
            ts.putback( t );
            return left;
        }
    }
}

void keep_window_open( )
{
    cout << "Enter a character to quit: ";
    char c{};
    cin >> c;
}

int main( )
{
    try {
        Token_stream ts{};

        while( true ) {
            cout << "> ";

            Token t{ ts.get( ) };
            // after entering our expression, there is a '\n'
            // in cin, causing primary( ) to throw
            // this will eat all the '\n', so we can enter
            // another expression
            while( t.type == print_ch )
                t = ts.get( );

            if( t.type == quit_ch ) break;
            else {
                ts.putback( t );
                cout << " = " << expression( ts ) << "\n";
            }
        }
    }
    catch( const char* exc ) {
        cout << "exception caught: " << exc << "\n";
        keep_window_open( );
        return 1;
    }
    catch( ... ) {
        cout << "unhandled exception caught\n";
        keep_window_open( );
        return 2;
    }

    keep_window_open( );
}


As you can see expression() calls term() which calls primary(). Through this, we can see that the order of operations is correct as each function deals with their own operators. This may be a lot to get your head around (it was confusing for me at first, but after explaining it to you I have a more thorough understanding).

Hope this helps :)
Last edited on
thank you very much!
Topic archived. No new replies allowed.