@@ -28,6 +28,7 @@ type MatchedValue =
2828type MatchedValueNode = {
2929 valueId : number ;
3030 value : MatchedValue ;
31+ wildcard : boolean ;
3132 prev : MatchedValueNode | undefined ;
3233} ;
3334
@@ -64,22 +65,35 @@ type MatchState = {
6465 | undefined ;
6566} ;
6667
67- function getMatchedValue (
68- valueId : ValueIdNode ,
68+ function getMatchedValueNode (
69+ valueId : number ,
6970 values : MatchedValueNode | undefined ,
70- ) : MatchedValue | undefined {
71+ ) : MatchedValueNode {
7172 let v : MatchedValueNode | undefined = values ;
72- while ( v !== undefined && v . valueId !== valueId . valueId ) {
73+ while ( v !== undefined && v . valueId !== valueId ) {
7374 v = v . prev ;
7475 }
75- return v ?. value ;
76+ if ( v === undefined ) {
77+ throw new Error ( `Internal error: Missing value for ${ valueId } ` ) ;
78+ }
79+ return v ;
7680}
7781
82+ type GrammarMatchStat = {
83+ matchedValueCount : number ;
84+ wildcardCharCount : number ;
85+ entityWildcardPropertyNames : string [ ] ;
86+ } ;
87+ export type GrammarMatchResult = GrammarMatchStat & {
88+ match : unknown ;
89+ } ;
90+
7891function createValue (
92+ stat : GrammarMatchStat ,
7993 node : ValueNode | undefined ,
8094 valueIds : ValueIdNode | undefined ,
8195 values : MatchedValueNode | undefined ,
82- ) : any {
96+ ) : unknown {
8397 if ( node === undefined ) {
8498 if ( valueIds === undefined ) {
8599 throw new Error ( "Internal error: default matched values" ) ;
@@ -89,9 +103,10 @@ function createValue(
89103 `Internal error: No value definitions for multiple values` ,
90104 ) ;
91105 }
92- const value = getMatchedValue ( valueIds , values ) ;
106+ const valueNode = getMatchedValueNode ( valueIds . valueId , values ) ;
107+ const value = valueNode . value ;
93108 if ( typeof value === "object" ) {
94- return createValue ( value . node , value . valueIds , values ) ;
109+ return createValue ( stat , value . node , value . valueIds , values ) ;
95110 }
96111 return value ;
97112 }
@@ -103,14 +118,14 @@ function createValue(
103118 const obj : Record < string , any > = { } ;
104119
105120 for ( const [ k , v ] of Object . entries ( node . value ) ) {
106- obj [ k ] = createValue ( v , valueIds , values ) ;
121+ obj [ k ] = createValue ( stat , v , valueIds , values ) ;
107122 }
108123 return obj ;
109124 }
110125 case "array" : {
111126 const arr : any [ ] = [ ] ;
112127 for ( const v of node . value ) {
113- arr . push ( createValue ( v , valueIds , values ) ) ;
128+ arr . push ( createValue ( stat , v , valueIds , values ) ) ;
114129 }
115130 return arr ;
116131 }
@@ -124,10 +139,26 @@ function createValue(
124139 `Internal error: No value for variable '${ node . name } . Values: ${ JSON . stringify ( valueIds ) } '` ,
125140 ) ;
126141 }
127- const value = getMatchedValue ( v , values ) ;
142+ const valueNode = getMatchedValueNode ( v . valueId , values ) ;
143+ const value = valueNode . value ;
128144 if ( typeof value === "object" ) {
129- return createValue ( value . node , value . valueIds , values ) ;
145+ return createValue ( stat , value . node , value . valueIds , values ) ;
146+ }
147+
148+ // undefined means optional, don't count
149+ if ( value !== undefined ) {
150+ stat . matchedValueCount ++ ;
130151 }
152+
153+ if ( valueNode . wildcard ) {
154+ if ( typeof value !== "string" ) {
155+ throw new Error (
156+ `Internal error: Wildcard has non-string value for variable '${ node . name } '` ,
157+ ) ;
158+ }
159+ stat . wildcardCharCount += value . length ;
160+ }
161+
131162 return value ;
132163 }
133164 }
@@ -157,14 +188,14 @@ function createCaptureWildcardState(
157188 newIndex : number ,
158189) {
159190 const { start : wildcardStart , valueId } = state . pendingWildcard ! ;
160- const wildcard = captureWildcard ( request , wildcardStart , wildcardEnd ) ;
161- if ( wildcard === undefined ) {
191+ const wildcardStr = captureWildcard ( request , wildcardStart , wildcardEnd ) ;
192+ if ( wildcardStr === undefined ) {
162193 return undefined ;
163194 }
164195 const newState = { ...state } ;
165196 newState . index = newIndex ;
166197 newState . pendingWildcard = undefined ;
167- addValueWithId ( newState , valueId , wildcard ) ;
198+ addValueWithId ( newState , valueId , wildcardStr , true ) ;
168199 return newState ;
169200}
170201
@@ -178,10 +209,12 @@ function addValueWithId(
178209 state : MatchState ,
179210 valueId : number ,
180211 matchedValue : MatchedValue ,
212+ wildcard : boolean ,
181213) {
182214 state . values = {
183215 valueId,
184216 value : matchedValue ,
217+ wildcard,
185218 prev : state . values ,
186219 } ;
187220}
@@ -192,13 +225,13 @@ function addValue(
192225 matchedValue : MatchedValue ,
193226) {
194227 const valueId = addValueId ( state , name ) ;
195- addValueWithId ( state , valueId , matchedValue ) ;
228+ addValueWithId ( state , valueId , matchedValue , false ) ;
196229}
197230
198231function finalizeRule (
199232 state : MatchState ,
200233 request : string ,
201- results : any [ ] ,
234+ results : GrammarMatchResult [ ] ,
202235 pending : MatchState [ ] ,
203236) {
204237 const nested = state . nested ;
@@ -239,7 +272,7 @@ function finalizeRule(
239272 return ;
240273 }
241274 state . index = request . length ;
242- addValueWithId ( state , state . pendingWildcard . valueId , value ) ;
275+ addValueWithId ( state , state . pendingWildcard . valueId , value , true ) ;
243276 }
244277 if ( state . index < request . length ) {
245278 // Detect trailing separators
@@ -261,18 +294,34 @@ function finalizeRule(
261294 debugMatch (
262295 `Matched at end of input. Matched ids: ${ JSON . stringify ( state . valueIds ) } , values: ${ JSON . stringify ( state . values ) } '` ,
263296 ) ;
264- results . push ( createValue ( state . rule . value , state . valueIds , state . values ) ) ;
297+
298+ const matchResult : GrammarMatchResult = {
299+ match : undefined ,
300+ matchedValueCount : 0 ,
301+ wildcardCharCount : 0 ,
302+ entityWildcardPropertyNames : [ ] ,
303+ } ;
304+ matchResult . match = createValue (
305+ matchResult ,
306+ state . rule . value ,
307+ state . valueIds ,
308+ state . values ,
309+ ) ;
310+ results . push ( matchResult ) ;
265311}
266312
267- type MatchResult = any ;
268- function matchRules ( grammar : Grammar , request : string ) : MatchResult [ ] {
313+ function matchRules ( grammar : Grammar , request : string ) : GrammarMatchResult [ ] {
269314 const pending : MatchState [ ] = grammar . rules . map ( ( r ) => ( {
270315 rule : r ,
271316 partIndex : 0 ,
272317 index : 0 ,
273318 nextValueId : 0 ,
319+ matchedCount : 0 ,
320+ wildcardCharCount : 0 ,
321+ nonOptionalCount : 0 ,
322+ implicitParameterCount : 0 ,
274323 } ) ) ;
275- const results : MatchResult [ ] = [ ] ;
324+ const results : GrammarMatchResult [ ] = [ ] ;
276325 while ( pending . length > 0 ) {
277326 const state = pending . shift ( ) ! ;
278327 const { rule, partIndex } = state ;
0 commit comments