|
4 | 4 | from ...support.coderefs import collector |
5 | 5 | from .epsilon_rules import is_epsilon_rule |
6 | 6 | from .exceptions import IncompatibleGrammarError |
7 | | -from .symbols import Terminal |
| 7 | +from .symbols import Terminal, new_non_terminal |
| 8 | +from .length_increasing import is_essentially_length_increasing_grammar |
| 9 | +from .grammar import GrammarRule, Grammar, GrammarSchema |
8 | 10 |
|
| 11 | +from .symbols import NonTerminal |
9 | 12 |
|
10 | | -if TYPE_CHECKING: |
11 | | - from .grammar import Grammar, GrammarSchema |
12 | | - from .symbols import NonTerminal |
13 | 13 |
|
| 14 | +def is_context_sensitive_rule(rule: GrammarRule) -> bool: |
| 15 | + for i in range(len(rule.src)): |
| 16 | + if not isinstance(rule.src[i], NonTerminal) or rule.src[:i] != rule.dest[:i]: |
| 17 | + continue |
14 | 18 |
|
15 | | -def is_length_increasing_grammar(grammar: Grammar) -> bool: |
16 | | - return all(len(rule.src) <= len(rule.dest) for rule in grammar.schema.rules) |
| 19 | + for j in range(len(rule.dest), i, -1): |
| 20 | + if rule.src[i + 1:] == rule.dest[j:]: |
| 21 | + return True |
| 22 | + |
| 23 | + return False |
17 | 24 |
|
18 | 25 |
|
19 | | -def is_essentially_length_increasing_grammar(grammar: Grammar) -> bool: |
| 26 | +def is_context_sensitive_grammar(grammar: Grammar) -> bool: |
20 | 27 | return all( |
21 | | - (is_epsilon_rule(rule) and rule.src == [grammar.start]) or len(rule.src) <= len(rule.dest) |
| 28 | + (is_epsilon_rule(rule) and rule.src == [grammar.start]) or is_context_sensitive_rule(rule) |
22 | 29 | for rule in grammar.schema.rules |
23 | 30 | ) |
24 | 31 |
|
25 | 32 |
|
26 | | -def _iter_all_derivations_one_step(schema: GrammarSchema, current: Iterable[Sequence[Terminal | NonTerminal]]) -> Iterable[Sequence[Terminal | NonTerminal]]: |
27 | | - for derivation in current: |
28 | | - for rule in schema.rules: |
29 | | - if is_epsilon_rule(rule): |
30 | | - continue |
| 33 | +@collector.ref('alg:length_increasing_to_context_sensitive') |
| 34 | +def length_increasing_to_context_sensitive(grammar: Grammar) -> Grammar: |
| 35 | + if not is_essentially_length_increasing_grammar(grammar): |
| 36 | + raise IncompatibleGrammarError('Expected an essentially length-increasing grammar') |
31 | 37 |
|
32 | | - for i in range(len(derivation) - len(rule.src) + 1): |
33 | | - if rule.src == derivation[i: len(rule.src) + i]: |
34 | | - yield [*derivation[:i], *rule.dest, *derivation[len(rule.src) + i:]] |
| 38 | + non_terminals = list(grammar.schema.get_non_terminals()) |
| 39 | + terminal_map = dict[Terminal, NonTerminal]() |
| 40 | + current_schema = GrammarSchema() |
35 | 41 |
|
| 42 | + for sym in grammar.schema.get_terminals(): |
| 43 | + active_non_terminal = new_non_terminal(sym.value, non_terminals) |
| 44 | + current_schema.rules.append( |
| 45 | + GrammarRule( |
| 46 | + src=[active_non_terminal], |
| 47 | + dest=[sym] |
| 48 | + ) |
| 49 | + ) |
36 | 50 |
|
37 | | -def iter_derivations(grammar: Grammar, max_derivation_length: int) -> Iterable[Sequence[Terminal]]: |
38 | | - if not is_essentially_length_increasing_grammar(grammar): |
39 | | - raise IncompatibleGrammarError('Expected an essentially ε-free grammar') |
| 51 | + terminal_map[sym] = active_non_terminal |
| 52 | + non_terminals.append(active_non_terminal) |
40 | 53 |
|
| 54 | + # We iterate over rules from the original grammar and modify the result at every step |
41 | 55 | for rule in grammar.schema.rules: |
42 | 56 | if is_epsilon_rule(rule): |
43 | | - yield [] |
44 | | - |
45 | | - derivable: Sequence[Sequence[Terminal | NonTerminal]] = [[grammar.start]] |
46 | | - |
47 | | - for _ in range(max_derivation_length): |
48 | | - derivable = list(_iter_all_derivations_one_step(grammar.schema, derivable)) |
49 | | - |
50 | | - for derivation in derivable: |
51 | | - if all(isinstance(sym, Terminal) for sym in derivation): |
52 | | - yield cast(Sequence[Terminal], derivation) |
53 | | - |
54 | | - |
55 | | -@collector.ref('alg:context_sensitive_string_membership') |
56 | | -def naive_membership(grammar: Grammar, string: str) -> bool: |
57 | | - if not is_essentially_length_increasing_grammar(grammar): |
58 | | - raise IncompatibleGrammarError('Expected an essentially ε-free grammar') |
59 | | - |
60 | | - m = len(grammar.schema.get_non_terminals()) + len(grammar.schema.get_terminals()) |
61 | | - n = len(string) |
62 | | - |
63 | | - max_derivation_length = (1 - m ** (n + 1)) // (1 - m) if m > 1 else n |
64 | | - |
65 | | - for derivation in iter_derivations(grammar, max_derivation_length): |
66 | | - if ''.join(sym.value for sym in derivation) == string: |
67 | | - return True |
68 | | - |
69 | | - return False |
| 57 | + current_schema.rules.append(rule) |
| 58 | + continue |
| 59 | + |
| 60 | + modified_src = [terminal_map[sym] if isinstance(sym, Terminal) else sym for sym in rule.src] |
| 61 | + modified_dest = [terminal_map[sym] if isinstance(sym, Terminal) else sym for sym in rule.dest] |
| 62 | + dest_tail = modified_dest[len(rule.src):] |
| 63 | + new_non_terminals = list[NonTerminal]() |
| 64 | + |
| 65 | + for i, sym in enumerate(rule.src): |
| 66 | + tail = modified_src[i + 1:] if i + 1 < len(rule.src) else dest_tail |
| 67 | + active_non_terminal = new_non_terminal(sym.value, non_terminals) |
| 68 | + current_schema.rules.append( |
| 69 | + GrammarRule( |
| 70 | + src=[*new_non_terminals, *modified_src[i:]], |
| 71 | + dest=[*new_non_terminals, active_non_terminal, *tail] |
| 72 | + ) |
| 73 | + ) |
| 74 | + |
| 75 | + new_non_terminals.append(active_non_terminal) |
| 76 | + non_terminals.append(active_non_terminal) |
| 77 | + |
| 78 | + for i in range(len(new_non_terminals)): |
| 79 | + current_schema.rules.append( |
| 80 | + GrammarRule( |
| 81 | + src=[*modified_dest[:i], *new_non_terminals[i:], *dest_tail], |
| 82 | + dest=[*modified_dest[:i + 1], *new_non_terminals[i + 1:], *dest_tail], |
| 83 | + ) |
| 84 | + ) |
| 85 | + |
| 86 | + print() |
| 87 | + print(current_schema) |
| 88 | + |
| 89 | + return current_schema.instantiate(grammar.start) |
0 commit comments