&gt;&gt; numPersons 2<p>&gt; I highly recommend not using formats like this, because it&#x27;s more complex than deterministic context-free.<p>Barely. It avoids the complexity of look-ahead and reallocating growing buffers.<p>&gt; A format that uses s-expressions would be safer and more easier to implement:<p>Trees are unsuitable for almost anything computationally, at least as authoritative representation. Prefer flat. (Widget hierarchies are one of a few counterexamples where the tradeoffs are different).<p>This s-expression you gave is much more complex to parse. Granted, you get parsing as a tree for free. But then you have to go inside the tree and see what&#x27;s there. That there are two persons per ancestor, etc. Navigating trees is hard.<p>&gt; Is it really &quot;trivial&quot;? Is it actually checking for all of the error conditions?<p>Yes. Yes. Of course. It&#x27;s the most boring code in the world. Parse numbers, allocate arrays correspondingly, parse each following table in a loop corresponding to the numbers parsed. You can do it all using scanf. Bail out as soon as a bad line comes in. 20 lines.<p>&gt; The benefit of using a parser generator is that those entire classes of bugs are impossible.<p>Yeah. Well, they are not impossible. They are only possible in the parser generator. And interfacing a parser generator is itself not necessarily trivial. Plus, dependencies are a maintenance burden.<p>The other approach is to make the design so simple (like the relational example) to make them (the classes) nonexistent.<p>&gt; You need at least one token of lookahead anyway.<p>Not in the example I gave.<p>&gt; Including the num* fields means the parser needs to remember and manage state.<p>I would hardly call that state. It&#x27;s an immutable number, initialized in sequential code without real data dependencies (the data dependencies are resolved in the format -- you can not go wrong parsing it).