1. Add a print
method to Expr
. The print
method should take a std::ostream&
argument and print the expression there. Print addition expressions with an infix +
and multiplication expressions with an infix *
. Every addition or multiplication expression should have parentheses around it, and there should be no extra space — not even a terminating newline.
Using parentheses for addition and multiplication ensures that the full structure of an expression is reflected by printing. For example, (1+(2+3))
corresponds to new Add(new Num(1), new Add(new Num(2), new Num(3)))
, while ((1+2)+3)
corresponds to new Add(new Add(new Num(1), new Num(2)), new Num(3))
. Those are different expressions, even though they will have the same value when we eventually interpret them.
Although it's not required, consider adding a (non-virtual
) to_string
method to Expr
. That method can call print
, but in many cases (like testing), getting a string back will be more convenient than setting up a std::ostream
for every test.
2. Add a pretty_print
method to Expr
. Like print
, it takes a std::ostream&
argument and prints there. Unlike print
, pretty_print
includes a space around +
or *
, and it avoids unnecessary parentheses by relying the usual precedence rules for multiplication and addition. It can also avoid parentheses by assuming that operators associate to the right. (Associating to the right is not necessarily as intuitive as associating to the left, but it will be a convenient choice in the near future.)
For example, the addition of 1 to the multiplication of 2 times 3 prints without any parentheses, because 2 times 3 is grouped by precedence: 1 + 2 * 3
. In contrast, the multiplication of 1 to the addition of 2 plus 3 needs parentheses around the 2 plus 3: 1 * (2 + 3)
. Similarly, 2 times 3 (quantity) multiplied by 4 needs parentheses to indicate that 2 and 3 are multiplied first: (2 * 3) * 4
. A 2 times the multiplication of 3 times 4 will not need parentheses, because multiplication will be treated as associating to the right: 2 * 3 * 4
.
Hint: You will likely find it useful to “accumulate” a precedence level for parentheses as an argument to a pretty_print_at
helper, where the precedence indicates which operations need parentheses: “none,” “addition or lower,” or “multiplication or lower.”
Hint 2: Here's the C++ way to declare a type precedence_t
that has the possible values prec_none
, prec_add
, and prec_mult
, in that order:
typedef enum {
prec_none, // = 0
prec_add, // = 1
prec_mult // = 2
} precedence_t;
In both parts of the assignment, don’t forget test cases. In class, expect to try your test cases against someone else’s implementation.