Would be really cool to have a close-to-human-readable grammar directly "building" the parser source...
I wonder what that "close-to-human-readable grammar" is?
How do I separate grammar definition from the transformation into AST nodes?
What you have is a handwritten Packrat Parser.
I could be wrong, but i understand this question as a request to use a standalone grammar definition to build a parser. And then use that parser to get the syntax tree of the parsed source.
So, the grammer could be an EBNF or PEG or CFG or "your own" grammar, right?
Anyway...
Lets start with a "separate grammar definition", e.g. EBNF.
Then you need a parser for the grammar, e.g. an EBNFParser
.
Parsing the grammar with that parser results is an internal structure of that grammar: a syntax tree.
Given a syntax tree for a valid grammar, you may return an association list with the keys (as the meta identifiers) and attach grammar rules to them.
foreach grammar key add matching grammar rule
That means that you need to pick a Grammar Rule identified by RuleName and add its Rule to a "Constructed Parser".
At the end: you have a "Constructed Parser" assembled from individual "Grammar Rules" able to parse Source defined by the given Grammar.
Parsing the Source, gives you a Syntax Tree for the source.
Pass 1
Grammar -> GrammarParser -> GrammarTree -> GrammarRules -> ConstructedParserForGrammar
Pass 2
Source -> ConstructedParserForGrammar -> Syntax Tree -> Transformations...
In other words: its quite a puzzle to go from BNF to an automatically constructed Packrat parser.