@@ -19,6 +19,7 @@ interface TypePart {
1919 ruleCalls : string [ ]
2020 parents : TypePart [ ]
2121 children : TypePart [ ]
22+ end ?: TypePart
2223 actionWithAssignment : boolean
2324}
2425
@@ -40,6 +41,7 @@ interface TypeCollectionContext {
4041
4142interface TypePath {
4243 alt : TypeAlternative
44+ current : TypePart
4345 next : TypePart [ ]
4446}
4547
@@ -53,20 +55,57 @@ class TypeGraph {
5355 }
5456
5557 getTypes ( ) : TypePath [ ] {
56- const rootType : TypeAlternative = {
57- name : this . root . name ! ,
58- properties : this . root . properties ,
59- ruleCalls : this . root . ruleCalls ,
60- super : [ ]
61- } ;
62- if ( this . root . children . length === 0 ) {
63- return [ { alt : rootType , next : [ ] } ] ;
64- } else {
65- return this . applyNext ( this . root , {
66- alt : rootType ,
67- next : this . root . children
68- } ) ;
58+ return this . iterate ( this . root , [ {
59+ alt : {
60+ name : this . root . name ! ,
61+ properties : this . root . properties ,
62+ ruleCalls : this . root . ruleCalls ,
63+ super : [ ]
64+ } ,
65+ current : this . root ,
66+ next : this . root . children
67+ } ] ) ;
68+ }
69+
70+ private iterate ( root : TypePart , paths : TypePath [ ] ) : TypePath [ ] {
71+ const finished = paths . filter ( e => e . next . length === 0 ) ;
72+ do {
73+ const next = this . recurse ( root , paths ) ;
74+ const unfinished : TypePath [ ] = [ ] ;
75+ for ( const path of next ) {
76+ if ( path . next . length > 0 ) {
77+ unfinished . push ( path ) ;
78+ } else {
79+ finished . push ( path ) ;
80+ }
81+ }
82+ paths = unfinished ;
83+ } while ( paths . length > 0 ) ;
84+
85+ return finished ;
86+ }
87+
88+ private recurse ( root : TypePart , paths : TypePath [ ] , end ?: TypePart ) : TypePath [ ] {
89+ const all : TypePath [ ] = [ ] ;
90+ for ( const path of paths ) {
91+ const node = path . current ;
92+ if ( node !== end && node . children . length > 0 ) {
93+ const nextPaths = this . applyNext ( root , path ) ;
94+ const subPaths = this . recurse ( root , nextPaths , node . end ?? end ) ;
95+ all . push ( ...subPaths ) ;
96+ } else {
97+ all . push ( path ) ;
98+ }
99+ }
100+ const map = new MultiMap < TypePart , TypePath > ( ) ;
101+ for ( const path of all ) {
102+ map . add ( path . current , path ) ;
69103 }
104+ const unique : TypePath [ ] = [ ] ;
105+ for ( const [ node , groupedPaths ] of map . entriesGroupedByKey ( ) ) {
106+ unique . push ( ...flattenTypes ( groupedPaths , node ) ) ;
107+ }
108+ return unique ;
70109 }
71110
72111 private applyNext ( root : TypePart , nextPath : TypePath ) : TypePath [ ] {
@@ -80,6 +119,7 @@ class TypeGraph {
80119 // We already add a new path, since the next part of the part refers to a new inferred type
81120 paths . push ( {
82121 alt : copyTypeAlternative ( split ) ,
122+ current : part ,
83123 next : [ ]
84124 } ) ;
85125 }
@@ -101,16 +141,13 @@ class TypeGraph {
101141 split . ruleCalls . push ( ...part . ruleCalls ) ;
102142 const path : TypePath = {
103143 alt : split ,
144+ current : part ,
104145 next : part . children
105146 } ;
106- if ( path . next . length === 0 ) {
107- path . alt . super = path . alt . super . filter ( e => e !== path . alt . name ) ;
108- paths . push ( path ) ;
109- } else {
110- paths . push ( ...this . applyNext ( root , path ) ) ;
111- }
147+ path . alt . super = path . alt . super . filter ( e => e !== path . alt . name ) ;
148+ paths . push ( path ) ;
112149 }
113- return flattenTypes ( paths ) ;
150+ return paths ;
114151 }
115152
116153 private splitType ( type : TypeAlternative , count : number ) : TypeAlternative [ ] {
@@ -307,9 +344,9 @@ function getRuleTypes(context: TypeCollectionContext, rule: ParserRule): TypePat
307344 const type = newTypePart ( rule ) ;
308345 const graph = new TypeGraph ( context , type ) ;
309346 if ( rule . definition ) {
310- collectElement ( graph , graph . root , rule . definition ) ;
347+ type . end = collectElement ( graph , graph . root , rule . definition ) ;
311348 }
312- return graph . getTypes ( ) ;
349+ return flattenTypes ( graph . getTypes ( ) , type . end ?? newTypePart ( ) ) ;
313350}
314351
315352function newTypePart ( element ?: ParserRule | Action | string ) : TypePart {
@@ -341,7 +378,9 @@ function collectElement(graph: TypeGraph, current: TypePart, element: AbstractEl
341378 const altType = graph . connect ( current , newTypePart ( ) ) ;
342379 children . push ( collectElement ( graph , altType , alt ) ) ;
343380 }
344- return graph . merge ( ...children ) ;
381+ const mergeNode = graph . merge ( ...children ) ;
382+ current . end = mergeNode ;
383+ return mergeNode ;
345384 } else if ( isGroup ( element ) || isUnorderedGroup ( element ) ) {
346385 let groupNode = graph . connect ( current , newTypePart ( ) ) ;
347386 let skipNode : TypePart | undefined ;
@@ -352,7 +391,9 @@ function collectElement(graph: TypeGraph, current: TypePart, element: AbstractEl
352391 groupNode = collectElement ( graph , groupNode , item ) ;
353392 }
354393 if ( skipNode ) {
355- return graph . merge ( skipNode , groupNode ) ;
394+ const mergeNode = graph . merge ( skipNode , groupNode ) ;
395+ current . end = mergeNode ;
396+ return mergeNode ;
356397 } else {
357398 return groupNode ;
358399 }
@@ -479,7 +520,9 @@ function getFragmentProperties(fragment: ParserRule, context: TypeCollectionCont
479520function calculateInterfaces ( alternatives : TypePath [ ] ) : PlainInterface [ ] {
480521 const interfaces = new Map < string , PlainInterface > ( ) ;
481522 const ruleCallAlternatives : TypeAlternative [ ] = [ ] ;
482- const flattened = flattenTypes ( alternatives ) . map ( e => e . alt ) ;
523+ const flattened = alternatives . length > 0
524+ ? flattenTypes ( alternatives , alternatives [ 0 ] . current ) . map ( e => e . alt )
525+ : [ ] ;
483526
484527 for ( const flat of flattened ) {
485528 const interfaceType : PlainInterface = {
@@ -516,14 +559,14 @@ function calculateInterfaces(alternatives: TypePath[]): PlainInterface[] {
516559 return Array . from ( interfaces . values ( ) ) ;
517560}
518561
519- function flattenTypes ( alternatives : TypePath [ ] ) : TypePath [ ] {
562+ function flattenTypes ( alternatives : TypePath [ ] , part : TypePart ) : TypePath [ ] {
520563 const nameToAlternatives = alternatives . reduce ( ( acc , e ) => acc . add ( e . alt . name , e ) , new MultiMap < string , TypePath > ( ) ) ;
521564 const types : TypePath [ ] = [ ] ;
522565
523566 for ( const [ name , namedAlternatives ] of nameToAlternatives . entriesGroupedByKey ( ) ) {
524567 const properties : PlainProperty [ ] = [ ] ;
525568 const ruleCalls = new Set < string > ( ) ;
526- const type : TypePath = { alt : { name, properties, ruleCalls : [ ] , super : [ ] } , next : [ ] } ;
569+ const type : TypePath = { alt : { name, properties, ruleCalls : [ ] , super : [ ] } , next : [ ] , current : part } ;
527570 for ( const path of namedAlternatives ) {
528571 const alt = path . alt ;
529572 type . alt . super . push ( ...alt . super ) ;
@@ -541,6 +584,7 @@ function flattenTypes(alternatives: TypePath[]): TypePath[] {
541584 alt . ruleCalls . forEach ( ruleCall => ruleCalls . add ( ruleCall ) ) ;
542585 }
543586 for ( const path of namedAlternatives ) {
587+ type . next = Array . from ( new Set ( type . next ) ) ;
544588 const alt = path . alt ;
545589 // A type with rule calls is not a real member of the type
546590 // Any missing properties are therefore not associated with the current type
0 commit comments