shift code

to place syntactic substitution and evaluation close to each other.
indent production rules to encapsulate them in parser class

because maintaining global state in the parser requires using either:

- global variables (less robust than creating a parser class)
- creating a parser class
- storing attributes in the parser's state which is made available to each production rule via its argument p, namely YaccProduction.parser. This approach is more implicit than using a parser class.

The parser class approach is also recommended by the PLY documentation, as "the easiest to manage" (with reference to lexer classes, but the same applies to parsers).

The state to be maintained is the networkx.DiGraph that will underly the AST in the future. A new graph need to be initialized for each formula parsed (so that the different trees returned will each have their own underlying nx graph instance).
use networkx.DiGraph as scaffold for AST

to use it for manipulating the tree, instead of rewriting partial implementations for specific functions. The main motivation for now is in-place replacement of AST nodes with a simple iteration over vertices, instead of writing yet one more graph traversal function specialized for our AST and the particular task.

The advantage comes from the fact that children information is stored only in the networkx.DiGraph. Each node does not store the edge relation. Instead, it keeps a pointer to the underlying graph and retrieves its children as its successors in that graph. The benefit is that the AST node can be replaced easily using the predecessor and successor information that networkx keeps track off (and avoiding to duplicate this by adding graph-theoretic methods to the AST classes).

Flattening is still preformed by the AST classes, not by a graph traversal function. The reason is that AST nodes exist as lightweight containers of syntax information and flattening is implemented generically for any syntax, allowing easy addition of more output formats.