@@ -79,22 +79,49 @@ func (g *Generator) getRule(ruleName string) *grammar.Rule {
7979
8080// generateQuery creates a single query using grammar rules
8181func (g * Generator ) generateQuery () string {
82- return g .generateFromRule (g .config .StartRule , 0 )
82+ // Start with no SCC context and 0 recursion depth
83+ return g .generateFromRuleWithSCC (g .config .StartRule , grammar .NoSCC , 0 )
8384}
8485
85- // generateFromRule generates text from a grammar rule
86+ // generateFromRule is a wrapper for backward compatibility
8687func (g * Generator ) generateFromRule (ruleName string , depth int ) string {
88+ // For backward compatibility, treat depth as recursion depth
89+ return g .generateFromRuleWithSCC (ruleName , grammar .NoSCC , depth )
90+ }
91+
92+ // generateFromRuleWithSCC generates text from a grammar rule tracking SCC-based recursion
93+ func (g * Generator ) generateFromRuleWithSCC (ruleName string , currentSCCID int , recursionDepth int ) string {
8794 // Get the rule and its SCC info
8895 rule := g .getRule (ruleName )
8996 if rule == nil {
9097 return fmt .Sprintf ("<%s>" , ruleName )
9198 }
9299
93100 node := g .dependencyGraph .GetNode (ruleName )
101+ if node == nil {
102+ return fmt .Sprintf ("<%s>" , ruleName )
103+ }
104+
105+ // Determine the new recursion depth
106+ // Only increment if we're moving within the same SCC (actual recursion)
107+ newRecursionDepth := recursionDepth
108+ if currentSCCID != grammar .NoSCC && node .SCCID == currentSCCID && node .IsRecursive {
109+ // We're recursing within the same SCC
110+ newRecursionDepth = recursionDepth + 1
111+
112+ // Check recursion depth limit
113+ if newRecursionDepth >= g .config .MaxDepth {
114+ return g .generateTerminalFallback (ruleName )
115+ }
116+ } else if node .IsRecursive {
117+ // Entering a new recursive SCC, reset recursion depth to 0
118+ newRecursionDepth = 0
119+ }
94120
95- // Check depth limit for recursive rules
96- if node != nil && node .IsRecursive && depth >= g .config .MaxDepth {
97- return g .generateTerminalFallback (ruleName )
121+ // Update current SCC context for recursive rules
122+ newSCCID := currentSCCID
123+ if node .IsRecursive {
124+ newSCCID = node .SCCID
98125 }
99126
100127 if len (rule .Alternatives ) == 0 {
@@ -108,7 +135,7 @@ func (g *Generator) generateFromRule(ruleName string, depth int) string {
108135 // Generate from all elements in the alternative
109136 var result []string
110137 for _ , element := range alternative .Elements {
111- elementResult := g .generateFromElement (& element , depth + 1 )
138+ elementResult := g .generateFromElementWithSCC (& element , newSCCID , newRecursionDepth )
112139 if elementResult != "" {
113140 result = append (result , elementResult )
114141 }
@@ -137,26 +164,31 @@ func (g *Generator) SetGrammarForTesting(grammar *grammar.ParsedGrammar) {
137164 g .dependencyGraph = grammar .GetDependencyGraph ()
138165}
139166
140- // generateFromElement generates text from a single grammar element
167+ // generateFromElement is a wrapper for backward compatibility
141168func (g * Generator ) generateFromElement (element * grammar.Element , depth int ) string {
169+ return g .generateFromElementWithSCC (element , grammar .NoSCC , depth )
170+ }
171+
172+ // generateFromElementWithSCC generates text from a single grammar element with SCC tracking
173+ func (g * Generator ) generateFromElementWithSCC (element * grammar.Element , currentSCCID int , recursionDepth int ) string {
142174 // Handle optional elements
143175 if element .IsOptional () && g .random .Float64 () > g .config .OptionalProb {
144176 return ""
145177 }
146178
147179 // Handle quantified elements
148180 if element .IsQuantified () {
149- return g .generateQuantified (element , depth )
181+ return g .generateQuantifiedWithSCC (element , currentSCCID , recursionDepth )
150182 }
151183
152184 // Generate single element
153185 if element .IsRule () {
154186 if refValue , ok := element .Value .(grammar.ReferenceValue ); ok {
155- return g .generateFromRuleOrToken (refValue .Name , depth )
187+ return g .generateFromRuleOrTokenWithSCC (refValue .Name , currentSCCID , recursionDepth )
156188 } else if blockValue , ok := element .Value .(grammar.BlockValue ); ok {
157- return g .generateFromBlock (blockValue , depth )
189+ return g .generateFromBlockWithSCC (blockValue , currentSCCID , recursionDepth )
158190 }
159- return g .generateFromRuleOrToken (element .Value .String (), depth )
191+ return g .generateFromRuleOrTokenWithSCC (element .Value .String (), currentSCCID , recursionDepth )
160192 } else if element .IsTerminal () {
161193 if litValue , ok := element .Value .(grammar.LiteralValue ); ok {
162194 return cleanLiteral (litValue .Text )
@@ -410,8 +442,8 @@ func joinStrings(strs []string, sep string) string {
410442 return result
411443}
412444
413- // generateQuantified handles quantified elements
414- func (g * Generator ) generateQuantified (element * grammar.Element , depth int ) string {
445+ // generateQuantifiedWithSCC handles quantified elements with SCC tracking
446+ func (g * Generator ) generateQuantifiedWithSCC (element * grammar.Element , currentSCCID int , recursionDepth int ) string {
415447 var count int
416448
417449 if g .config .QuantifierCount > 0 {
@@ -431,13 +463,13 @@ func (g *Generator) generateQuantified(element *grammar.Element, depth int) stri
431463 for i := 0 ; i < count ; i ++ {
432464 if element .IsRule () {
433465 if refValue , ok := element .Value .(grammar.ReferenceValue ); ok {
434- result := g .generateFromRuleOrToken (refValue .Name , depth )
466+ result := g .generateFromRuleOrTokenWithSCC (refValue .Name , currentSCCID , recursionDepth )
435467 results = append (results , result )
436468 } else if blockValue , ok := element .Value .(grammar.BlockValue ); ok {
437- result := g .generateFromBlock (blockValue , depth )
469+ result := g .generateFromBlockWithSCC (blockValue , currentSCCID , recursionDepth )
438470 results = append (results , result )
439471 } else {
440- result := g .generateFromRuleOrToken (element .Value .String (), depth )
472+ result := g .generateFromRuleOrTokenWithSCC (element .Value .String (), currentSCCID , recursionDepth )
441473 results = append (results , result )
442474 }
443475 } else if element .IsTerminal () {
@@ -452,8 +484,13 @@ func (g *Generator) generateQuantified(element *grammar.Element, depth int) stri
452484 return joinWithSpaces (results )
453485}
454486
455- // generateFromBlock generates content from a block value
487+ // generateFromBlock is a wrapper for backward compatibility
456488func (g * Generator ) generateFromBlock (blockValue grammar.BlockValue , depth int ) string {
489+ return g .generateFromBlockWithSCC (blockValue , grammar .NoSCC , depth )
490+ }
491+
492+ // generateFromBlockWithSCC generates content from a block value with SCC tracking
493+ func (g * Generator ) generateFromBlockWithSCC (blockValue grammar.BlockValue , currentSCCID int , recursionDepth int ) string {
457494 if len (blockValue .Alternatives ) == 0 {
458495 return ""
459496 }
@@ -463,7 +500,7 @@ func (g *Generator) generateFromBlock(blockValue grammar.BlockValue, depth int)
463500
464501 var result []string
465502 for _ , element := range alternative .Elements {
466- elementResult := g .generateFromElement (& element , depth )
503+ elementResult := g .generateFromElementWithSCC (& element , currentSCCID , recursionDepth )
467504 if elementResult != "" {
468505 result = append (result , elementResult )
469506 }
@@ -472,12 +509,13 @@ func (g *Generator) generateFromBlock(blockValue grammar.BlockValue, depth int)
472509 return joinWithSpaces (result )
473510}
474511
475- // generateFromRuleOrToken generates from a rule or token
476- func (g * Generator ) generateFromRuleOrToken (ruleName string , depth int ) string {
512+ // generateFromRuleOrTokenWithSCC generates from a rule or token with SCC tracking
513+ func (g * Generator ) generateFromRuleOrTokenWithSCC (ruleName string , currentSCCID int , recursionDepth int ) string {
477514 if rule := g .grammar .GetRule (ruleName ); rule != nil && rule .IsLexer {
478- return g .generateConcreteToken (ruleName , depth )
515+ // Lexer rules don't participate in SCC recursion tracking
516+ return g .generateConcreteToken (ruleName , 0 )
479517 }
480- return g .generateFromRule (ruleName , depth )
518+ return g .generateFromRuleWithSCC (ruleName , currentSCCID , recursionDepth )
481519}
482520
483521// generateSimpleFallback generates a simple fallback value based on rule name patterns
0 commit comments