@@ -11,15 +11,16 @@ export enum Component {
1111 Group ,
1212}
1313
14+ // Higher number is higher precedence
1415export enum Combinator {
1516 /** Components are mandatory and should appear in that order */
16- Juxtaposition ,
17+ Juxtaposition = 0 ,
1718 /** Components are mandatory but may appear in any order */
18- DoubleAmpersand ,
19+ DoubleAmpersand = 1 ,
1920 /** At least one of the components must be present, and they may appear in any order */
20- DoubleBar ,
21+ DoubleBar = 2 ,
2122 /** Exactly one of the components must be present */
22- SingleBar ,
23+ SingleBar = 3 ,
2324}
2425
2526export enum Multiplier {
@@ -72,7 +73,6 @@ export type ComponentType = INonGroupData | IGroupData;
7273
7374export interface ICombinator {
7475 entity : Entity . Combinator ;
75- multiplier : MultiplierType | null ;
7676 combinator : Combinator ;
7777}
7878
@@ -81,7 +81,7 @@ export interface IFunction {
8181 multiplier : MultiplierType | null ;
8282}
8383
84- interface IUnknown {
84+ export interface IUnknown {
8585 entity : Entity . Unknown ;
8686 multiplier : MultiplierType | null ;
8787}
@@ -92,10 +92,29 @@ const REGEX_ENTITY = /(?:^|\s)((?:[\w]+\([^\)]*\))|[^\s*+?#!{]+)([*+?#!]|{(\d+),
9292const REGEX_DATA_TYPE = / ^ ( < [ ^ > ] + > ) / g;
9393const REGEX_KEYWORD = / ^ ( [ \w - ] + ) / g;
9494
95+ const combinators : { [ key : number ] : ICombinator } = {
96+ [ Combinator . Juxtaposition ] : {
97+ entity : Entity . Combinator ,
98+ combinator : Combinator . Juxtaposition ,
99+ } ,
100+ [ Combinator . DoubleAmpersand ] : {
101+ entity : Entity . Combinator ,
102+ combinator : Combinator . DoubleAmpersand ,
103+ } ,
104+ [ Combinator . DoubleBar ] : {
105+ entity : Entity . Combinator ,
106+ combinator : Combinator . DoubleBar ,
107+ } ,
108+ [ Combinator . SingleBar ] : {
109+ entity : Entity . Combinator ,
110+ combinator : Combinator . SingleBar ,
111+ } ,
112+ } ;
113+
95114export default function parse ( syntax : string ) : EntityType [ ] {
96115 const levels : EntityType [ ] [ ] = [ [ ] ] ;
97- const deepestLevel = ( ) => levels [ levels . length - 1 ] ;
98116 let previousMatchWasComponent = false ;
117+
99118 let entityMatch : RegExpExecArray | null ;
100119 while ( ( entityMatch = REGEX_ENTITY . exec ( syntax ) ) ) {
101120 const [ , value , ...rawMultiplier ] = entityMatch ;
@@ -104,27 +123,27 @@ export default function parse(syntax: string): EntityType[] {
104123 previousMatchWasComponent = false ;
105124 continue ;
106125 } else if ( value . indexOf ( '&&' ) === 0 ) {
107- deepestLevel ( ) . push ( combinatorData ( Combinator . DoubleAmpersand , multiplierData ( rawMultiplier ) ) ) ;
126+ deepestLevel ( ) . push ( combinators [ Combinator . DoubleAmpersand ] ) ;
108127 previousMatchWasComponent = false ;
109128 continue ;
110129 } else if ( value . indexOf ( '||' ) === 0 ) {
111- deepestLevel ( ) . push ( combinatorData ( Combinator . DoubleBar , multiplierData ( rawMultiplier ) ) ) ;
130+ deepestLevel ( ) . push ( combinators [ Combinator . DoubleBar ] ) ;
112131 previousMatchWasComponent = false ;
113132 continue ;
114133 } else if ( value . indexOf ( '|' ) === 0 ) {
115- deepestLevel ( ) . push ( combinatorData ( Combinator . SingleBar , multiplierData ( rawMultiplier ) ) ) ;
134+ deepestLevel ( ) . push ( combinators [ Combinator . SingleBar ] ) ;
116135 previousMatchWasComponent = false ;
117136 continue ;
118137 } else if ( value . indexOf ( ']' ) === 0 ) {
119138 const definitions = levels . pop ( ) ;
120139 if ( definitions ) {
121- deepestLevel ( ) . push ( componentGroupData ( definitions , multiplierData ( rawMultiplier ) ) ) ;
140+ deepestLevel ( ) . push ( componentGroupData ( groupByPrecedence ( definitions ) , multiplierData ( rawMultiplier ) ) ) ;
122141 }
123142 previousMatchWasComponent = true ;
124143 continue ;
125144 } else {
126- if ( previousMatchWasComponent === true ) {
127- deepestLevel ( ) . push ( combinatorData ( Combinator . Juxtaposition ) ) ;
145+ if ( previousMatchWasComponent ) {
146+ deepestLevel ( ) . push ( combinators [ Combinator . Juxtaposition ] ) ;
128147 }
129148
130149 if ( value . indexOf ( '[' ) === 0 ) {
@@ -149,15 +168,54 @@ export default function parse(syntax: string): EntityType[] {
149168 deepestLevel ( ) . push ( { entity : Entity . Unknown , multiplier : multiplierData ( rawMultiplier ) } ) ;
150169 }
151170
152- return levels [ 0 ] ;
171+ function deepestLevel ( ) {
172+ return levels [ levels . length - 1 ] ;
173+ }
174+
175+ return groupByPrecedence ( levels [ 0 ] ) ;
153176}
154177
155- function combinatorData ( combinator : Combinator , multiplier : MultiplierType | null = null ) : ICombinator {
156- return {
157- entity : Entity . Combinator ,
158- combinator,
159- multiplier,
160- } ;
178+ export function isFunction ( entity : EntityType ) : entity is IFunction {
179+ return entity . entity === Entity . Function ;
180+ }
181+
182+ export function isComponent ( entity : EntityType ) : entity is ComponentType {
183+ return entity . entity === Entity . Component ;
184+ }
185+
186+ export function isCombinator ( entity : EntityType ) : entity is ICombinator {
187+ return entity . entity === Entity . Combinator ;
188+ }
189+
190+ export function isCurlyBracetMultiplier ( multiplier : MultiplierType ) : multiplier is IMultiplierCurlyBracet {
191+ return multiplier . sign === Multiplier . CurlyBracet ;
192+ }
193+
194+ export function isMultiplied ( multiplier : MultiplierType ) {
195+ return (
196+ ( isCurlyBracetMultiplier ( multiplier ) && ( multiplier . min > 1 || multiplier . max > 1 ) ) ||
197+ multiplier . sign === Multiplier . Asterisk ||
198+ multiplier . sign === Multiplier . PlusSign ||
199+ multiplier . sign === Multiplier . HashMark ||
200+ multiplier . sign === Multiplier . ExclamationPoint
201+ ) ;
202+ }
203+
204+ export function isMandatoryEntity ( entity : EntityType ) {
205+ if ( isCombinator ( entity ) ) {
206+ return entity === combinators [ Combinator . DoubleAmpersand ] || entity === combinators [ Combinator . Juxtaposition ] ;
207+ }
208+
209+ if ( entity . multiplier ) {
210+ return (
211+ ( isCurlyBracetMultiplier ( entity . multiplier ) && entity . multiplier . min === 0 ) ||
212+ entity . multiplier . sign === Multiplier . PlusSign ||
213+ entity . multiplier . sign === Multiplier . HashMark ||
214+ entity . multiplier . sign === Multiplier . ExclamationPoint
215+ ) ;
216+ }
217+
218+ return true ;
161219}
162220
163221function componentData (
@@ -173,7 +231,7 @@ function componentData(
173231 } ;
174232}
175233
176- function componentGroupData ( entities : EntityType [ ] , multiplier : MultiplierType | null ) : ComponentType {
234+ function componentGroupData ( entities : EntityType [ ] , multiplier : MultiplierType | null = null ) : ComponentType {
177235 return {
178236 entity : Entity . Component ,
179237 component : Component . Group ,
@@ -198,8 +256,63 @@ function multiplierData(raw: string[]): MultiplierType | null {
198256 case '!' :
199257 return { sign : Multiplier . ExclamationPoint } ;
200258 case '{' :
201- return { sign : Multiplier . CurlyBracet , min : + raw [ 1 ] , max : + raw [ 2 ] } ;
259+ return { sign : Multiplier . CurlyBracet , min : Number ( raw [ 1 ] ) , max : Number ( raw [ 2 ] ) } ;
202260 default :
203261 return null ;
204262 }
205263}
264+
265+ function groupByPrecedence ( entities : EntityType [ ] , precedence : number = Combinator . SingleBar ) : EntityType [ ] {
266+ if ( precedence < 0 ) {
267+ // We've reached the lowest precedence possible
268+ return entities ;
269+ }
270+
271+ const combinator = combinators [ precedence ] ;
272+ const combinatorIndexes : number [ ] = [ ] ;
273+
274+ // Search for indexes where the combinator is used
275+ for ( let i = entities . indexOf ( combinator ) ; i > - 1 ; i = entities . indexOf ( combinator , i + 1 ) ) {
276+ combinatorIndexes . push ( i ) ;
277+ }
278+
279+ const nextPrecedence = precedence - 1 ;
280+
281+ if ( combinatorIndexes . length === 0 ) {
282+ return groupByPrecedence ( entities , nextPrecedence ) ;
283+ }
284+
285+ const groupedEntities : EntityType [ ] = [ ] ;
286+
287+ // Yes, what you see is correct: it's index of indexes
288+ for (
289+ let i = 0 ;
290+ // Add one loop to finnish up the last entities
291+ i < combinatorIndexes . length + 1 ;
292+ i ++
293+ ) {
294+ const sectionEntities = entities . slice (
295+ i > 0
296+ ? combinatorIndexes [ i - 1 ] + 1
297+ : // Slice from beginning
298+ 0 ,
299+ i < combinatorIndexes . length
300+ ? combinatorIndexes [ i ]
301+ : // Slice to end
302+ entities . length ,
303+ ) ;
304+
305+ // Only group if there's more than one entity in between
306+ if ( sectionEntities . length > 1 ) {
307+ groupedEntities . push ( componentGroupData ( groupByPrecedence ( sectionEntities , nextPrecedence ) ) ) ;
308+ } else {
309+ groupedEntities . push ( ...sectionEntities ) ;
310+ }
311+
312+ if ( i < combinatorIndexes . length ) {
313+ groupedEntities . push ( entities [ combinatorIndexes [ i ] ] ) ;
314+ }
315+ }
316+
317+ return groupedEntities ;
318+ }
0 commit comments