In an object-oriented world dominated by languages like Java and C#, option 1 is the way to go. There are different ways you can model your data, for example another way is with a tree that can be walked. But sticking with the spirit of your question, there are two approaches to consider. In both cases it is important to use interfaces. Writing programs on top of abstractions is far more flexible than writing them on top of concrete implementations.
The first approach is to extract common code into abstract super classes. This is really easy to do with languages like Java and C#, and at first glance seems like the obvious choice. The main problem with this approach is that it is too easy to conflate inheritance with the abstractions you are trying to create. I argue that these abstract classes created entirely to share common, but otherwise unimportant to inheritance, code are part of of the implementation, and thus shouldn't be relied upon in client code. This may or may not be a problem, but in either case there is nothing that can be done to prevent it from happening.
To remedy this, the second approach would be to to favor composition over abstract classes. While this is not as easy to implement as the first, it does address the problem left by abstract classes. Namely, there can be no reliance in the artifacts left by the implementation details of the abstract classes, as long as you properly encapsulate the composed objects. If, however, the abstract classes (if they even need to be abstract) are important to the inheritance hierarchy, and are thus more than just "common code buckets", then favoring composition will reduce the usefulness of the classes.
In the end I think it depends on whether the abstract classes are important to building a proper abstraction based on inheritance, or if the common code is just there to ease the burden of maintaining it. It may be flat-out annoying to implement if there are a lot of methods that just delegate to an encapsulated object, but that trade-off might be worth it for something more pure. However, some languages such as Ruby make it easy to support composition with its modules, which are basically mixins.