Our first thought in terms of a class design is to represent each query operation as an individual class:
NameQuery // Alice NotQuery // ! Alice OrQuery // Alice || fiery AndQuery // Alice && Daddy
At first blush, this design appears adequate. For example, each class provides an eval() method that solves the query for the operation it represents, and a display_solution() method to display its solution. A solution is represented as a unique collection of line numbers in ascending order.
The eval() method for NameQuery simply returns the numbers of the lines in which the name occurs. The eval() method for OrQuery, on the other hand, must build up a union of the line occurrences of its two operands. The AndQuery ...