@@ -25,9 +25,14 @@ export type Expr =
2525export interface Alt {
2626 type : 'Alt' ;
2727 children : Expr [ ] ;
28+ outArity : number ;
2829}
2930
30- export const alt = ( children : Expr [ ] ) : Alt => ( { type : 'Alt' , children} ) ;
31+ export const alt = ( children : Expr [ ] ) : Alt => ( {
32+ type : 'Alt' ,
33+ children,
34+ outArity : outArity ( children [ 0 ] )
35+ } ) ;
3136
3237export interface Any {
3338 type : 'Any' ;
@@ -91,17 +96,27 @@ export const end = (): End => ({type: 'End'});
9196export interface Lex {
9297 type : 'Lex' ;
9398 child : Expr ;
99+ outArity : number ;
94100}
95101
96- export const lex = ( child : Expr ) : Lex => ( { type : 'Lex' , child} ) ;
102+ export const lex = ( child : Expr ) : Lex => ( {
103+ type : 'Lex' ,
104+ child,
105+ outArity : outArity ( child )
106+ } ) ;
97107
98108// TODO: Eliminate this, and replace with Not(Not(...))?
99109export interface Lookahead {
100110 type : 'Lookahead' ;
101111 child : Expr ;
112+ outArity : number ;
102113}
103114
104- export const lookahead = ( child : Expr ) : Lookahead => ( { type : 'Lookahead' , child} ) ;
115+ export const lookahead = ( child : Expr ) : Lookahead => ( {
116+ type : 'Lookahead' ,
117+ child,
118+ outArity : outArity ( child )
119+ } ) ;
105120
106121export interface Opt {
107122 type : 'Opt' ;
@@ -142,9 +157,14 @@ export const range = (lo: number, hi: number): Range => ({type: 'Range', lo, hi}
142157export interface Seq {
143158 type : 'Seq' ;
144159 children : Expr [ ] ;
160+ outArity : number ;
145161}
146162
147- export const seq = ( children : Expr [ ] ) : Seq => ( { type : 'Seq' , children} ) ;
163+ export const seq = ( children : Expr [ ] ) : Seq => ( {
164+ type : 'Seq' ,
165+ children,
166+ outArity : children . reduce ( ( acc , child ) => acc + outArity ( child ) , 0 )
167+ } ) ;
148168
149169export interface Star {
150170 type : 'Star' ;
@@ -247,15 +267,24 @@ export function substituteParams(exp: Expr, actuals: Expr[]) {
247267 case 'Seq' :
248268 return {
249269 type : exp . type ,
250- children : exp . children . map ( c => substituteParams ( c , actuals ) )
270+ children : exp . children . map ( c => substituteParams ( c , actuals ) ) ,
271+ outArity : exp . outArity
251272 } ;
252273 case 'Lex' :
253274 case 'Lookahead' :
275+ return {
276+ type : exp . type ,
277+ child : substituteParams ( exp . child , actuals ) ,
278+ outArity : exp . outArity
279+ } ;
254280 case 'Not' :
255281 case 'Opt' :
256282 case 'Plus' :
257283 case 'Star' :
258- return { type : exp . type , child : substituteParams ( exp . child , actuals ) } ;
284+ return {
285+ type : exp . type ,
286+ child : substituteParams ( exp . child , actuals )
287+ } ;
259288 case 'Any' :
260289 case 'ApplyGeneralized' :
261290 case 'CaseInsensitive' :
@@ -297,8 +326,9 @@ export function rewrite(exp: Expr, actions: RewriteActions): Expr {
297326
298327 switch ( exp . type ) {
299328 case 'Alt' :
329+ return alt ( exp . children . map ( ( e : Expr ) => rewrite ( e , actions ) ) ) ;
300330 case 'Seq' :
301- return { type : exp . type , children : exp . children . map ( ( e : Expr ) => rewrite ( e , actions ) ) } ;
331+ return seq ( exp . children . map ( ( e : Expr ) => rewrite ( e , actions ) ) ) ;
302332 case 'Any' :
303333 case 'Apply' :
304334 case 'ApplyGeneralized' :
@@ -311,14 +341,20 @@ export function rewrite(exp: Expr, actions: RewriteActions): Expr {
311341 case 'UnicodeChar' :
312342 return exp ;
313343 case 'Dispatch' :
344+ // We don't use the constructor here, to avoid type checking issues.
314345 return { type : exp . type , child : rewrite ( exp . child , actions ) , patterns : exp . patterns } ;
315346 case 'Lex' :
347+ return lex ( rewrite ( exp . child , actions ) ) ;
316348 case 'Lookahead' :
349+ return lookahead ( rewrite ( exp . child , actions ) ) ;
317350 case 'Not' :
351+ return not ( rewrite ( exp . child , actions ) ) ;
318352 case 'Opt' :
353+ return opt ( rewrite ( exp . child , actions ) ) ;
319354 case 'Plus' :
355+ return plus ( rewrite ( exp . child , actions ) ) ;
320356 case 'Star' :
321- return { type : exp . type , child : rewrite ( exp . child , actions ) } ;
357+ return star ( rewrite ( exp . child , actions ) ) ;
322358 default :
323359 unreachable ( exp , `not handled: ${ exp } ` ) ;
324360 }
@@ -370,3 +406,38 @@ export function toString(exp: Expr): string {
370406 unreachable ( exp , `not handled: ${ exp } ` ) ;
371407 }
372408}
409+
410+ // Returns the number of bindings that `expr` produces in its parent — the
411+ // "out arity" or "upwards arity". Note that there is potential confusion
412+ // with iter nodes: they produce a single binding, but an expression like
413+ // `(letter digit)*` can be said to have "arity 2".
414+ export function outArity ( exp : Expr ) : number {
415+ console . log ( JSON . stringify ( exp ) ) ;
416+ switch ( exp . type ) {
417+ case 'Alt' :
418+ case 'Seq' :
419+ case 'Lex' :
420+ case 'Lookahead' :
421+ return exp . outArity ;
422+ case 'Any' :
423+ case 'Apply' :
424+ case 'ApplyGeneralized' :
425+ case 'CaseInsensitive' :
426+ case 'End' :
427+ case 'LiftedTerminal' :
428+ case 'Param' :
429+ case 'Range' :
430+ case 'Terminal' :
431+ case 'UnicodeChar' :
432+ case 'Dispatch' :
433+ return 1 ;
434+ case 'Not' :
435+ return 0 ;
436+ case 'Opt' :
437+ case 'Plus' :
438+ case 'Star' :
439+ return 1 ;
440+ default :
441+ unreachable ( exp , `not handled: ${ exp } ` ) ;
442+ }
443+ }
0 commit comments