mbutterick / brag

Racket DSL for generating parsers from BNF grammars [moved to https://git.matthewbutterick.com/mbutterick/brag]
https://git.matthewbutterick.com/mbutterick/brag
MIT License
61 stars 12 forks source link

Rule aliasing #36

Open jackfirth opened 2 years ago

jackfirth commented 2 years ago

I've got a grammar where expressions are split between two rules: inline-expression and block-expression. They have to be split because in some contexts only inline expressions are allowed and in others only block expressions are allowed. However, I want them to both produce (expression ...) nodes in the resulting parse trees. Can brag be extended with some sort of feature for this? I'm picturing something that lets people write grammar rules like this:

#lang brag

expression.inline: ...
expression.block: ...

somerule: ... expression.inline ...
someotherrule: ... expression.block ...

...where expression.inline and expression.block are different rules, but they both produce (expression ...) nodes instead of producing (expression.inline ...) and (expression.block ...) nodes.

mbutterick commented 2 years ago

Can brag be extended with some sort of feature for this?

Can’t brag already do this? Define expression in terms of your subrules, and then splice the names of the subrules globally. In the parse tree, the terminals belonging to any inline-expression or block-expression will be hoisted into the surrounding expression node (fragment below):

expression: inline-expression | block-expression
@inline-expression: [rule pattern ···]
@block-expression: [another rule pattern ···]
jackfirth commented 2 years ago

No, because when somerule refers to inline-expression it will get the (inline-expression ...) wrapper. There's no way for somerule to refer to a rule named expression while specifying "actually I only meant this kind of expression."

mbutterick commented 2 years ago

OK, I see what you mean. In that case — can’t make-rename-transformer be used (after the parse, within the expander) to convert uses of expression.inline and expression.block into expression?

In general, if the macro expander already has a good solution for task X, I’m unlikely to be persuaded to invent a brag-specific notational solution for that task. For instance, the reason brag has notational cuts and splices is that I discovered that those transformations are difficult & annoying to accomplish within the macro expander.

jackfirth commented 2 years ago

That's what I would do for an ordinary language, but I'm trying to make a macro-extensible language with brag so the parse trees I spit out from read-syntax will be visible to userland macro code. I don't want userland macro code to start to use those aliases and then break if I refactor or rename my grammar rules. So I have to do this postprocessing before handing my syntax objects off to the macro expander. I can get very far with cuts and splices, but this one task is tricky to do within read-syntax and outside brag. I have to traverse the syntax tree and swap out and rename things while making sure I copy over source locations and properties just right.