The way you designed your Nodes/Terms seems perfectly natural to me and is basically how you would model the thing in C anyway.
Each node in the expression would have a tag to specify its type and then it would have some values that are particular for its type.
> I've ended up with towering class hierarchies
Looking at what you showed us so far, I don't see any "towering class hierarchies". I just see sensible representation of data.
The one small "weirdness" is you have "Number" as a root but it's not doing anything. You don't need it. You already have a way to represent numbers with the `Integer` type.
What I would do is use Term as the basic representation every where, so for example, the fraction type would be:
class Fraction : Term
You can also have a much simpler representation: only two types of nodes:
class BinaryTerm : Term
Where `Operation` could be an enum.
The other class would be a leaf for representing the actual integers:
class Number: Term