Here is the replacement for the original rule: aux pattern ---- rule: { seq(?nested-rule) ... } => { seq(?nested-rule) ... } { choice(?nested-rule) ... } => { choice(?nested-rule) ... } { many(?nested-rule) ... } => { many(?nested-rule) ... } { opt(?nested-rule) ... } => { opt(?nested-rule) ... } { opt-seq(?nested-rule) ... } => { opt-seq(?nested-rule) ... } { opt-choice(?nested-rule) ... } => { opt-choice(?nested-rule) ... } { opt-many(?nested-rule) ... } => { opt-many(?nested-rule) ... } { req-next(?nested-rule) ... } => { req-next(?nested-rule) ... } { not-next(?nested-rule) ... } => { not-next(?nested-rule) ... } { ?:name ... } => { "parse-" ## ?name ... } { ?:token ... } => { ?token ... } { } => { } nested-rule: { ?rule } => { ?rule } ---- Aux patterns must match everything, and recursion into an expression can be done via nested-rule as I show here.