First things first, I would like to re-iterate Christophe's suggestion: if you can, somehow, re-architect your code so that the grammar description you use in the documentation is also the parser (or the parser is generated from it), that would be the safest way going forward to make sure they never get out of sync.
Going the other way is probably not going to be possible. In the general case, deriving the grammar from the parser is going to be equivalent to solving the Halting Problem.
So, you will have to restrict the way you write your parser by following strict naming conventions, maybe use decorators or annotations hidden in comments to help the documentation generator along.
An alternative to trying to automatically generate the grammar description from the parser code that is much more tractable is to put the grammar description into decorators, comments, or docstrings of the parser methods themselves. That way, the grammar fragments are documented right next to the code that implements that part of the grammar, and thus the "mental distance" is reduced and it is much easier to edit the code and the grammar at the same time, if they are only 2-3 lines away from each other and clearly visible on screen.
A last method that can both be applied to your current solution as well as combined with any of Christophe's or my suggestions, would be instead of generating a parser from the grammar description, to generate a test harness from the grammar description with both positive and negative test cases and make sure that
- all of the generated positive test cases are accepted (giving you confidence there are no valid programs that are rejected by your parser)
- the positive test cases exercise 100% of your parser code (giving you confidence that the positive test cases explore the space of legal programs thoroughly)
- none of the generated negative test cases are accepted
If you combine this with mutation testing, you can be reasonably sure that your parser and your grammar match.
Note, however, that generating a program that generates valid utterances in your grammar from the grammar description may be as hard as or even harder than generating a parser in the first place.
As an alternative (or in addition to any or all of the above), you could hand-write instead of generate plenty of positive and negative examples in your documentation, and automatically extract those into a test suite.
An extreme example of this kind of "doctest" testing is the book Agile Web Development with Rails. A couple of years ago, Sam Ruby spent several months (if not more than a year) re-architecting how the book is written so that the entire book can be "run" cover to cover as a gigantic system test suite for Ruby on Rails. For example, all terminal commands in the book are marked up explicitly, and the test harness extracts the commands and the output from the book, runs the commands and compares their output with the captured output from the book. The same applies to code on the interactive Ruby REPL.
Basically, the test harness will follow the book in the same way a reader would, execute all the steps a reader would take, and make sure that the results are as described in the book.
This can be used in two "directions" (in addition to simply making sure that there are no bugs in the book):
- When a new major release of Rails comes out, it documents all the cases where behavior has changed and the book needs updating. (In fact, the test harness can actually be run in a mode where it updates the terminal outputs and interactive REPL return values automatically.)
- When a new minor release comes out, it can serve as a regression test for Rails to make sure the behavior hasn't changed. (The book is considered to be the "canonical" in-depth tutorial for Rails and almost like specification of Rails' features.)
You can apply those same ideas to the examples in your documentation.