Begin with a simple grammar:
expr: n-expr {o-expr} | p-expr {o-expr}
n-expr: [0-9]n-expr
p-expr: ( expr )
o-expr: op expr
op: + | - | * | /
This is probably the largest hurdle for the question. You want to be able to write a simple top down recursive descent parser, so your grammar needs to be written in a way to allow that to happen.
Then, the implementation from there is fairly straightforward:
bool expr (const char *&s, int &result, int eos = 0) {
while (isspace(*s)) ++s;
if (*s == eos) return false;
if (isdigit(*s)) {
if (!n_expr(s, result)) return false;
} else if (*s == '(') {
if (!p_expr(s, result)) return false;
} else return false;
while (isspace(*s)) ++s;
if (*s == eos) return true;
return o_expr(s, result, eos);
}
bool n_expr (const char *&s, int &result) {
int n = 0;
while (isdigit(*s)) n = 10 * n + (*s++ - '0');
result = n;
return true;
}
bool p_expr (const char *&s, int &result) {
if (expr(++s, result, ')')) {
++s;
return true;
}
return false;
}
bool o_expr (const char *&s, int &result, int eos) {
int oresult = 0;
const char *op = strchr("+-*/", *s);
if (op == 0) return false;
if (!expr(++s, oresult, eos)) return false;
switch (*op) {
case '+': result += oresult; break;
case '-': result -= oresult; break;
case '*': result *= oresult; break;
case '/': result /= oresult; break;
default: return false;
}
return true;
}