@@ -112,6 +112,19 @@ def _symbols_of(
112
112
else :
113
113
return [[other ]]
114
114
115
+ def is_any (self ) -> bool :
116
+ if isinstance (self , sym .Symbol ):
117
+ return False
118
+ if len (self ) == 1 :
119
+ return self .item (0 ).is_any ()
120
+ return self is Expression .ANY
121
+
122
+ def is_eps (self ) -> bool :
123
+ return isinstance (self , sym .Symbol ) and self .symbol .is_eps ()
124
+
125
+ def is_nor (self ) -> bool :
126
+ return isinstance (self , sym .Symbol ) and self .symbol .is_nor ()
127
+
115
128
def accepts (
116
129
self , other : 'Expression.OR_SYMBOL' , equivalent : bool = False
117
130
) -> bool :
@@ -124,6 +137,10 @@ def accepts(
124
137
Returns:
125
138
bool
126
139
"""
140
+ if self .is_any () or other .is_any ():
141
+ if equivalent :
142
+ return self .is_any () and other .is_any ()
143
+ return True
127
144
self_symbols , other_symbols = self .symbols (), self ._symbols_of (other )
128
145
self_len , other_len = len (self_symbols ), len (other_symbols )
129
146
if (
@@ -198,6 +215,8 @@ def contains_symbol_list(
198
215
return True
199
216
if search_for == [sym .Symbol .CTRL .nor ]:
200
217
return False
218
+ if self .is_any ():
219
+ return True
201
220
# Loop over symbol lists, eg: [[a, b, c, d], [e, f, g]]
202
221
for symbol_list in self .symbols ():
203
222
while symbol_list :
@@ -251,15 +270,19 @@ def contains(
251
270
a.contains(b, head=True): [a, b, c, d] starts with [a, b]
252
271
a.contains(b, tail=True): False
253
272
"""
273
+ if self .is_any () or other .is_any ():
274
+ return not self .is_nor () and not other .is_nor ()
254
275
for sym_list in self ._symbols_of (other ):
255
276
if self .contains_symbol_list (sym_list , head , tail ):
256
277
return True
257
278
return False
258
279
259
280
def _symbol_contains (self , other : sym .Symbol ) -> bool :
281
+ if other .is_any ():
282
+ return True
260
283
self_symbols = self .symbols ()
261
284
return [sym .Symbol .CTRL .eps ] in self_symbols or (
262
- other != sym . Symbol . CTRL . nor and [other ] in self_symbols
285
+ not other . is_nor () and [other ] in self_symbols
263
286
)
264
287
265
288
def is_contained (
@@ -278,13 +301,13 @@ def matches(self, other: 'Expression.OR_SYMBOL') -> bool:
278
301
return self .contains (other , head = True , tail = True )
279
302
280
303
# head_matches and tail_matches require at least one symbol match unless
281
- # both expressions are empty Cats. For example, if a rule requires a vowel as
282
- # following context but there is no following context, the rule shouldn't
283
- # apply.
304
+ # both expressions are empty Cats or one of the expressions is Expression.ANY
305
+ # For example, if a rule requires a vowel as following context but there is no
306
+ # following context, the rule shouldn't apply.
284
307
285
308
def head_matches (self , other : 'Expression.OR_SYMBOL' ) -> bool :
286
309
if self and not other :
287
- return False
310
+ return other . is_any ()
288
311
return self .contains (other , head = True )
289
312
290
313
def is_prefix (self , other : 'Expression.OR_SYMBOL' ) -> bool :
@@ -294,7 +317,7 @@ def is_prefix(self, other: 'Expression.OR_SYMBOL') -> bool:
294
317
295
318
def tail_matches (self , other : 'Expression.OR_SYMBOL' ) -> bool :
296
319
if self and not other :
297
- return False
320
+ return other . is_any ()
298
321
return self .contains (other , tail = True )
299
322
300
323
def is_suffix (self , other : 'Expression.OR_SYMBOL' ) -> bool :
@@ -316,6 +339,9 @@ def repeat(self, n: int = 2) -> 'Cat':
316
339
return Cat (* ([self ] * n ))
317
340
318
341
342
+ Expression .ANY = Expression ('any_expression' )
343
+
344
+
319
345
class Atomic (Expression , sym .Symbol ):
320
346
"""An instance of a single symbol."""
321
347
@@ -392,7 +418,10 @@ def __str__(self):
392
418
393
419
def add (self , * items : Expression ) -> 'Cat' :
394
420
for item in items :
395
- self ._add_item (item )
421
+ if item .is_any ():
422
+ self ._items .append (item )
423
+ else :
424
+ self ._add_item (item )
396
425
return self
397
426
398
427
def symbols (self ) -> list [list [sym .Symbol ]]:
@@ -462,6 +491,13 @@ def add(self, *items: Expression) -> 'Or':
462
491
self
463
492
"""
464
493
for item in items :
494
+ # If the Expression.ANY is in Or, don't add any items.
495
+ if Expression .ANY in self :
496
+ break
497
+ # If the item is any, the other items are irrelevant.
498
+ if item .is_any ():
499
+ self ._items = [item ]
500
+ break
465
501
if self .accepts (item ):
466
502
if item .accepts (self ) and item .state_count () < self .state_count ():
467
503
self ._update (item )
0 commit comments