def _selection_share_common(sel: Antlr4Selection, start_index): assert start_index == 0 or start_index == -1 assert isinstance(sel, Antlr4Selection), sel.__class__ # find options which starting with the same element with_item = {i: set([ i, ]) for i in range(len(sel))} for i0, p_opt in enumerate(sel): for i1, opt in enumerate(islice(sel, i0 + 1, None)): if p_opt and opt and p_opt[start_index] == opt[start_index]: prefix_cls = with_item[i0] prefix_cls.update(with_item[i0 + i1 + 1]) with_item[i0 + i1 + 1] = prefix_cls resolved = set() new_options = [] changed = False for i, _prefix_eq_cls in sorted(with_item.items()): if i in resolved: continue if len(_prefix_eq_cls) == 1: # keep option as it is new_opt = sel[i] else: changed = True # extraction of shared prefix eq_cls = list(sorted(_prefix_eq_cls)) extracted_item = sel[i][start_index] if start_index == 0: new_opt = Antlr4Sequence([ extracted_item, Antlr4Selection( [Antlr4Sequence(sel[i0][1:]) for i0 in eq_cls]) ]) else: assert start_index == -1 new_opt = Antlr4Sequence([ Antlr4Selection( [Antlr4Sequence(sel[i0][:-1]) for i0 in eq_cls]), extracted_item ]) resolved.update(eq_cls) new_options.append(new_opt) if len(new_options) == 1: return new_options[0], changed else: sel.clear() sel.extend(new_options) return sel, changed
def _selection_reduce_optional(o: Antlr4Selection): """ a? | b? -> (a | b)? """ assert isinstance(o, Antlr4Selection) non_optional_items = [] for c in o: if isinstance(c, Antlr4Sequence): if len(c) != 1: return o, False c = c[0] if not isinstance(c, Antlr4Option): return o, False non_optional_items.append(c.body) o.clear() o.extend(non_optional_items) return Antlr4Sequence([ Antlr4Option(o), ]), True