File Coverage

blib/lib/Grammar/Graph.pm
Criterion Covered Total %
statement 104 613 16.9
branch 0 72 0.0
condition 0 51 0.0
subroutine 35 99 35.3
pod 7 44 15.9
total 146 879 16.6


line stmt bran cond sub pod time code
1             #####################################################################
2             # Types
3             #####################################################################
4             package Grammar::Graph::Types;
5 1     1   83274 use Modern::Perl;
  1         8435  
  1         6  
6 1     1   528 use parent qw/Type::Library/;
  1         237  
  1         4  
7 1     1   20193 use Type::Utils;
  1         3889  
  1         10  
8 1     1   1636 use Types::Standard qw/Int/;
  1         40589  
  1         13  
9              
10             declare 'Vertex',
11             as Int,
12             where { $_ > 0 };
13              
14             #####################################################################
15             # Role for non-terminal names
16             #####################################################################
17             package Grammar::Graph::Named;
18 1     1   906 use Modern::Perl;
  1         3  
  1         11  
19 1     1   515 use Moose::Role;
  1         405254  
  1         6  
20              
21             has 'name' => (
22             is => 'ro',
23             required => 1,
24             isa => 'Str'
25             );
26              
27             #####################################################################
28             # Role for coupled vertices
29             #####################################################################
30             package Grammar::Graph::Coupled;
31 1     1   4743 use Modern::Perl;
  1         2  
  1         7  
32 1     1   91 use Moose::Role;
  1         1  
  1         4  
33              
34             has 'partner' => (
35             is => 'ro',
36             required => 1,
37             writer => '_set_partner',
38             isa => Grammar::Graph::Types::Vertex(),
39             );
40              
41             #####################################################################
42             # Start
43             #####################################################################
44             package Grammar::Graph::Start;
45 1     1   4323 use Modern::Perl;
  1         2  
  1         4  
46 1     1   70 use Moose;
  1         2  
  1         4  
47             extends 'Grammar::Formal::Empty';
48             with 'Grammar::Graph::Coupled',
49             'Grammar::Graph::Named';
50            
51             #####################################################################
52             # Final
53             #####################################################################
54             package Grammar::Graph::Final;
55 1     1   6171 use Modern::Perl;
  1         2  
  1         4  
56 1     1   79 use Moose;
  1         2  
  1         5  
57             extends 'Grammar::Formal::Empty';
58             with 'Grammar::Graph::Coupled',
59             'Grammar::Graph::Named';
60              
61             #####################################################################
62             # Conditionals
63             #####################################################################
64             package Grammar::Graph::Conditional;
65 1     1   5638 use Modern::Perl;
  1         2  
  1         5  
66 1     1   63 use Moose;
  1         2  
  1         4  
67              
68             extends qw/Grammar::Formal::Empty/;
69             with qw/Grammar::Graph::Coupled/;
70              
71             has 'p1' => (
72             is => 'ro',
73             required => 1,
74             isa => Grammar::Graph::Types::Vertex()
75             );
76              
77             has 'p2' => (
78             is => 'ro',
79             required => 1,
80             isa => Grammar::Graph::Types::Vertex()
81             );
82              
83             has 'name' => (
84             is => 'ro',
85             required => 1,
86             isa => 'Str'
87             );
88              
89             #####################################################################
90             # If (start of conditional)
91             #####################################################################
92             package Grammar::Graph::If;
93 1     1   5892 use Modern::Perl;
  1         2  
  1         5  
94 1     1   82 use Moose;
  1         2  
  1         4  
95             extends 'Grammar::Graph::Conditional';
96              
97             #####################################################################
98             # Fi (end of conditional)
99             #####################################################################
100             package Grammar::Graph::Fi;
101 1     1   5948 use Modern::Perl;
  1         2  
  1         4  
102 1     1   68 use Moose;
  1         3  
  1         4  
103             extends 'Grammar::Graph::Conditional';
104              
105             #####################################################################
106             # Operands
107             #####################################################################
108             package Grammar::Graph::Operand;
109 1     1   5716 use Modern::Perl;
  1         2  
  1         4  
110 1     1   86 use Moose;
  1         2  
  1         9  
111             extends 'Grammar::Formal::Empty';
112             with qw/Grammar::Graph::Coupled/;
113              
114             #####################################################################
115             # Prelude (character before any other)
116             #####################################################################
117             package Grammar::Graph::Prelude;
118 1     1   5556 use Modern::Perl;
  1         2  
  1         4  
119 1     1   68 use Moose;
  1         2  
  1         11  
120             extends 'Grammar::Formal::CharClass';
121             with qw/Grammar::Graph::Coupled/;
122              
123             has '+spans' => (
124             required => 0,
125             default => sub {
126             Set::IntSpan->new([-1])
127             },
128             );
129              
130             #####################################################################
131             # Postlude (character after any other)
132             #####################################################################
133             package Grammar::Graph::Postlude;
134 1     1   6610 use Modern::Perl;
  1         3  
  1         3  
135 1     1   68 use Moose;
  1         1  
  1         4  
136             extends 'Grammar::Formal::CharClass';
137             with qw/Grammar::Graph::Coupled/;
138              
139             has '+spans' => (
140             required => 0,
141             default => sub {
142             Set::IntSpan->new([-1])
143             },
144             );
145              
146             #####################################################################
147             # Grammar::Graph
148             #####################################################################
149             package Grammar::Graph;
150 1     1   5419 use 5.012000;
  1         3  
151 1     1   4 use Modern::Perl;
  1         1  
  1         4  
152 1     1   574 use Grammar::Formal;
  1         250852  
  1         46  
153 1     1   436 use List::UtilsBy qw/partition_by/;
  1         1299  
  1         65  
154 1     1   649 use List::MoreUtils qw/uniq/;
  1         6317  
  1         6  
155 1     1   863 use List::Util qw/shuffle sum max/;
  1         2  
  1         61  
156 1     1   408 use Storable qw/freeze thaw/;
  1         2247  
  1         62  
157 1     1   273 use Graph::SomeUtils qw/:all/;
  1         78271  
  1         171  
158 1     1   356 use Graph::Directed;
  1         177  
  1         22  
159 1     1   6 use Moose;
  1         2  
  1         8  
160              
161             #####################################################################
162             # Globals
163             #####################################################################
164              
165             local $Storable::canonical = 1;
166              
167             our $VERSION = '0.20';
168              
169             our %EXPORT_TAGS = ( 'all' => [ qw(
170            
171             ) ] );
172              
173             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
174              
175             our @EXPORT = qw(
176             );
177              
178             #####################################################################
179             # Attributes
180             #####################################################################
181              
182             has 'g' => (
183             is => 'ro',
184             required => 1,
185             isa => 'Graph::Directed',
186             default => sub { Graph::Directed->new },
187             );
188              
189             has 'symbol_table' => (
190             is => 'ro',
191             required => 1,
192             isa => 'HashRef',
193             default => sub { {} },
194             );
195              
196             has 'start_vertex' => (
197             is => 'ro',
198             required => 0, # FIXME?
199             writer => '_set_start_vertex',
200             isa => Grammar::Graph::Types::Vertex(),
201             );
202              
203             has 'final_vertex' => (
204             is => 'ro',
205             required => 0, # FIXME?
206             writer => '_set_final_vertex',
207             isa => Grammar::Graph::Types::Vertex(),
208             );
209              
210             has 'pattern_converters' => (
211             is => 'ro',
212             required => 1,
213             isa => 'HashRef[CodeRef]',
214             default => sub { {
215             'Grammar::Formal::CharClass' => \&convert_char_class,
216             'Grammar::Formal::ProseValue' => \&convert_prose_value,
217             'Grammar::Formal::Reference' => \&convert_reference,
218             'Grammar::Formal::NotAllowed' => \&convert_not_allowed,
219              
220             'Grammar::Formal::Range' => \&convert_range,
221             'Grammar::Formal::AsciiInsensitiveString'
222             => \&convert_ascii_insensitive_string,
223             'Grammar::Formal::CaseSensitiveString'
224             => \&convert_case_sensitive_string,
225              
226             'Grammar::Formal::Grammar' => \&convert_grammar,
227             'Grammar::Formal' => \&convert_grammar_formal,
228             'Grammar::Formal::Rule' => \&convert_rule,
229              
230             'Grammar::Formal::BoundedRepetition'
231             => \&convert_bounded_repetition,
232              
233             'Grammar::Formal::SomeOrMore' => \&convert_some_or_more,
234             'Grammar::Formal::OneOrMore' => \&convert_one_or_more,
235             'Grammar::Formal::ZeroOrMore' => \&convert_zero_or_more,
236              
237             'Grammar::Formal::Empty' => \&convert_empty,
238              
239             'Grammar::Formal::Group' => \&convert_group,
240              
241             'Grammar::Formal::Choice' => \&convert_choice,
242             'Grammar::Formal::Conjunction' => \&convert_conjunction,
243             'Grammar::Formal::Subtraction' => \&convert_subtraction,
244              
245             'Grammar::Formal::OrderedChoice' => \&convert_ordered_choice,
246             'Grammar::Formal::OrderedConjunction'
247             => \&convert_ordered_conjunction,
248              
249             } },
250             );
251              
252             sub reversed_copy {
253 0     0 0   my ($self) = @_;
254              
255 0           my $g = Graph::Directed->new;
256              
257 0           $g->add_edge(reverse @$_) for $self->g->edges;
258              
259 0           my $copy = $self->new(%{ $self }, g => $g);
  0            
260              
261 0           for my $v ($self->g->vertices) {
262 0           my $label = $self->get_vertex_label($v);
263 0 0         next unless $label;
264 0           if (0 && UNIVERSAL::can($label, 'partner')) {
265             my $cloned = $label->new(%$label, partner => $v);
266             $copy->set_vertex_label($label->partner, $cloned);
267             } else {
268 0           my $cloned = $label->new(%$label);
269 0           $copy->set_vertex_label($v, $cloned);
270             }
271             }
272              
273 0           $copy->_set_start_vertex($self->final_vertex);
274 0           $copy->_set_final_vertex($self->start_vertex);
275              
276 0           return $copy;
277             }
278              
279             #####################################################################
280             # Helper functions
281             #####################################################################
282             sub _copy_predecessors {
283 0     0     my ($self, $src, $dst) = @_;
284             $self->g->add_edge($_, $dst)
285 0           for $self->g->predecessors($src);
286             }
287              
288             sub _copy_successors {
289 0     0     my ($self, $src, $dst) = @_;
290             $self->g->add_edge($dst, $_)
291 0           for $self->g->successors($src);
292             }
293              
294             sub _find_endpoints {
295 0     0     my ($self, $id) = @_;
296              
297 0           my $symbols = $self->symbol_table;
298 0           my $start = $symbols->{$id}{start_vertex};
299 0           my $final = $symbols->{$id}{final_vertex};
300            
301 0           return ($start, $final);
302             }
303              
304             #####################################################################
305             # ...
306             #####################################################################
307              
308             sub register_converter {
309 0     0 0   my ($self, $class, $code) = @_;
310 0           $self->pattern_converters->{$class} = $code;
311             }
312              
313             sub find_converter {
314 0     0 0   my ($self, $pkg) = @_;
315 0           return $self->pattern_converters->{$pkg};
316             }
317              
318             #####################################################################
319             # ...
320             #####################################################################
321              
322             sub _fa_next_id {
323 0     0     my ($self) = @_;
324            
325 0           my $next_id = $self->g->get_graph_attribute('fa_next_id');
326            
327 0 0 0       $next_id = do {
328 0   0       my $max = max(grep { /^[0-9]+$/ } $self->g->vertices) // 0;
  0            
329 0           $max + 1;
330             } if not defined $next_id or $self->g->has_vertex($next_id);
331              
332 0           $self->g->set_graph_attribute('fa_next_id', $next_id + 1);
333              
334 0           return $next_id;
335             }
336              
337             sub fa_add_state {
338 0     0 1   my ($self, %o) = @_;
339            
340 0   0       my $expect = $o{p} // Grammar::Formal::Empty->new;
341            
342 0           my $id = $self->_fa_next_id();
343 0           $self->g->add_vertex($id);
344 0 0         $self->set_vertex_label($id, $expect)
345             if defined $expect;
346              
347 0           return $id;
348             }
349              
350             sub fa_all_e_reachable {
351 0     0 1   my ($self, $v) = @_;
352 0           my %seen;
353 0           my @todo = ($v);
354 0           while (@todo) {
355 0           my $c = pop @todo;
356 0 0         next if $self->is_terminal_vertex($c);
357 0           push @todo, grep { not $seen{$_}++ } $self->g->successors($c);
  0            
358             }
359 0           keys %seen;
360             }
361              
362             # from => $vertex,
363             # want => sub { ... },
364             # next => sub { ... },
365              
366             # self => 'always|never|if_reachable'
367             # vertex_if => sub { ... }
368             # successors_if => sub { ... }
369              
370             sub all_reachable {
371 0     0 0   my ($g, $source, $cond) = @_;
372 0   0 0     $cond //= sub { 1 };
  0            
373 0           my %seen;
374 0           my @todo = ($source);
375 0           my %ok;
376 0           while (defined(my $v = pop @todo)) {
377 0           $ok{$_}++ for $g->successors($v);
378             push @todo, grep {
379 0 0         $cond->($_) and not $seen{$_}++
  0            
380             } $g->successors($v);
381             }
382 0           keys %ok;
383             };
384              
385             #####################################################################
386             # Helper function to clone label when cloning subgraph
387             #####################################################################
388             sub _clone_label {
389 0     0     my ($self, $label, $want, $map) = @_;
390              
391 0 0         return unless UNIVERSAL::can($label, 'meta');
392              
393 0           my %ref_vertex_map;
394              
395 0           for my $att ($label->meta->get_all_attributes) {
396              
397 0           my $tc = $att->type_constraint;
398              
399 0 0         next unless $tc;
400 0 0         next unless $tc->equals(Grammar::Graph::Types::Vertex());
401              
402             warn "Trying to clone subgraph without cloning label vertices (" . $att->name . ")"
403 0 0         unless $want->{ $att->get_value($label) };
404              
405 0   0       $map->{ $att->get_value($label) } //= $self->fa_add_state();
406              
407             $ref_vertex_map{ $att->name } =
408 0           $map->{ $att->get_value($label) };
409             }
410              
411 0           return $label->new(%$label, %ref_vertex_map)
412             }
413              
414             #####################################################################
415             # Clone a subgraph between two vertices
416             #####################################################################
417             sub _clone_subgraph_between {
418 0     0     my ($self, $src, $dst) = @_;
419              
420 0           my %want = map { $_ => 1 }
  0            
421             graph_vertices_between($self->g, $src, $dst);
422              
423 0           my %map;
424            
425 0           for my $k (keys %want) {
426              
427 0   0       $map{$k} //= $self->fa_add_state();
428              
429 0           my $label = $self->get_vertex_label($k);
430 0           my $cloned_label = _clone_label($self, $label, \%want, \%map);
431              
432 0   0       $self->set_vertex_label($map{$k},
433             $cloned_label // $label);
434             }
435              
436 0           while (my ($old, $new) = each %map) {
437 0           for (grep { $want{$_} } $self->g->successors($old)) {
  0            
438 0           $self->g->add_edge($new, $map{$_});
439             }
440             }
441            
442 0           return ($map{$src}, $map{$dst}, \%map);
443             }
444              
445             sub _clone_non_terminal {
446 0     0     my ($self, $id) = @_;
447             return $self->_clone_subgraph_between(
448             $self->symbol_table->{$id}{start_vertex},
449             $self->symbol_table->{$id}{final_vertex},
450 0           );
451             }
452              
453             #####################################################################
454             # Generate a graph with all rules with edges over ::References
455             #####################################################################
456             sub _fa_ref_graph {
457 0     0     my ($self) = @_;
458 0           my $symbols = $self->symbol_table;
459 0           my $ref_graph = Graph::Directed->new;
460              
461 0           for my $r1 (keys %$symbols) {
462 0           my $v = $symbols->{$r1};
463 0           for (graph_all_successors_and_self($self->g, $v->{start_vertex})) {
464 0 0         next unless $self->vertex_isa($_, 'Grammar::Formal::Reference');
465 0           my $label = $self->get_vertex_label($_);
466 0           my $r2 = $label->expand;
467 0           $ref_graph->add_edge("$r1", "$r2");
468             # $ref_graph->add_edge("$r1", "$_");
469             # $ref_graph->add_edge("$_", "$r2");
470             }
471             }
472              
473 0           return $ref_graph;
474             }
475              
476             #####################################################################
477             # ...
478             #####################################################################
479             sub fa_expand_one_by_copying {
480 0     0 0   my ($self, $id) = @_;
481              
482             my %id_to_refs = partition_by {
483 0     0     $self->get_vertex_label($_)->expand . ''
484             } grep {
485 0           $self->vertex_isa($_, 'Grammar::Formal::Reference')
  0            
486             } $self->g->vertices;
487              
488 0           for my $v (@{ $id_to_refs{$id} }) {
  0            
489 0           my $label = $self->get_vertex_label($v);
490              
491 0           my ($src, $dst) = $self->_clone_non_terminal($id);
492              
493 0           $self->_copy_predecessors($v, $src);
494 0           $self->_copy_successors($v, $dst);
495 0           graph_delete_vertex_fast($self->g, $v);
496             }
497             }
498              
499             sub fa_expand_references {
500 0     0 1   my ($self) = @_;
501 0           my $symbols = $self->symbol_table;
502              
503 0           my $ref_graph = $self->_fa_ref_graph;
504 0           my $scg = $ref_graph->strongly_connected_graph;
505              
506 0           my @topo = grep { not $ref_graph->has_edge($_, $_) }
  0            
507             reverse $scg->toposort;
508              
509 0           for my $id (@topo) {
510             # NOTE: Relies on @topo containing invalid a+b+c+... IDs
511 0           $self->fa_expand_one_by_copying($id);
512             }
513              
514 0           for my $v ($self->g->vertices) {
515 0           my $label = $self->get_vertex_label($v);
516              
517 0 0         next unless $self->vertex_isa($v, 'Grammar::Formal::Reference');
518              
519 0           my $id = $label->expand;
520              
521             # TODO: explain
522             # TODO: remove
523             # next if $scg->has_vertex("$id")
524             # && !$ref_graph->has_edge("$id", "$id");
525              
526 0           my $v1 = $self->fa_add_state();
527 0           my $v2 = $self->fa_add_state();
528              
529 0           my $name = $label->expand->name;
530              
531 0           my $p1 = Grammar::Graph::Start->new(
532             partner => $v2, name => $name);
533            
534 0           my $p2 = Grammar::Graph::Final->new(
535             partner => $v1, name => $name);
536              
537 0           $self->set_vertex_label($v1, $p1);
538 0           $self->set_vertex_label($v2, $p2);
539              
540 0           my ($start, $final) = $self->_find_endpoints($id);
541              
542 0           $self->_copy_predecessors($v, $v1);
543 0           $self->_copy_successors($start, $v1);
544              
545 0           $self->_copy_successors($v, $v2);
546 0           $self->_copy_predecessors($final, $v2);
547            
548 0           graph_delete_vertex_fast($self->g, $v);
549             }
550              
551 0           for my $v ($self->g->vertices) {
552 0 0         die if $self->vertex_isa($v, 'Grammar::Formal::Reference');
553             }
554              
555             }
556              
557             #####################################################################
558             # Encapsulate ...
559             #####################################################################
560              
561             sub _find_id_by_shortname {
562 0     0     my ($self, $shortname) = @_;
563              
564 0           for my $k (keys %{ $self->symbol_table }) {
  0            
565 0 0         next unless $self->symbol_table->{$k}{shortname} eq $shortname;
566 0           return $k;
567             }
568             }
569              
570             sub fa_prelude_postlude {
571 0     0 0   my ($self, $shortname) = @_;
572              
573 0           my $s1 = $self->fa_add_state();
574 0           my $s2 = $self->fa_add_state();
575              
576 0           my $sS = $self->fa_add_state();
577 0           my $sF = $self->fa_add_state();
578              
579 0           my $p1 = Grammar::Graph::Prelude->new(partner => $s2);
580 0           my $p2 = Grammar::Graph::Postlude->new(partner => $s1);
581              
582 0           my $pS = Grammar::Graph::Start->new(name => "", partner => $sF);
583 0           my $pF = Grammar::Graph::Final->new(name => "", partner => $sS);
584              
585 0           $self->set_vertex_label($s1, $p1);
586 0           $self->set_vertex_label($s2, $p2);
587              
588 0           $self->set_vertex_label($sS, $pS);
589 0           $self->set_vertex_label($sF, $pF);
590              
591 0           my $id = _find_id_by_shortname($self, $shortname);
592              
593 0 0         die unless defined $id;
594              
595 0           my $rd = $self->symbol_table->{$id};
596              
597             =pod
598              
599             _copy_predecessors($self, $rd->{start_vertex}, $s1);
600             _copy_successors($self, $rd->{start_vertex}, $s1);
601             graph_isolate_vertex($self->g, $rd->{start_vertex});
602              
603             _copy_predecessors($self, $rd->{final_vertex}, $s2);
604             _copy_successors($self, $rd->{final_vertex}, $s2);
605             graph_isolate_vertex($self->g, $rd->{final_vertex});
606              
607             $self->g->add_edge($rd->{start_vertex}, $s1);
608             $self->g->add_edge($s2, $rd->{final_vertex});
609              
610             =cut
611              
612 0           $self->g->add_edge($sS, $s1);
613 0           $self->g->add_edge($s1, $rd->{start_vertex});
614 0           $self->g->add_edge($rd->{final_vertex}, $s2);
615 0           $self->g->add_edge($s2, $sF);
616              
617 0           $self->_set_start_vertex($sS);
618 0           $self->_set_final_vertex($sF);
619             }
620              
621             #####################################################################
622             # Remove unlabeled vertices
623             #####################################################################
624             sub fa_remove_useless_epsilons {
625 0     0 1   my ($graph, @todo) = @_;
626 0           my %deleted;
627              
628 0           for my $v (sort @todo) {
629 0           my $label = $graph->get_vertex_label($v);
630 0 0 0       next if defined $label and ref($label) ne 'Grammar::Formal::Empty';
631 0 0         next unless $graph->g->successors($v); # FIXME(bh): why?
632 0 0         next unless $graph->g->predecessors($v); # FIXME(bh): why?
633 0           for my $src ($graph->g->predecessors($v)) {
634 0           for my $dst ($graph->g->successors($v)) {
635 0           $graph->g->add_edge($src, $dst);
636             }
637             }
638 0           $deleted{$v}++;
639             }
640 0           graph_delete_vertices_fast($graph->g, keys %deleted);
641             };
642              
643             #####################################################################
644             # Merge character classes
645             #####################################################################
646             sub fa_merge_character_classes {
647 0     0 1   my ($self) = @_;
648            
649             my %groups = partition_by {
650 0     0     freeze [
651             [sort $self->g->predecessors($_)],
652             [sort $self->g->successors($_)]
653             ];
654             } grep {
655 0           my $label = $self->get_vertex_label($_);
  0            
656 0 0         $label and $label->isa('Grammar::Formal::CharClass');
657             } $self->g->vertices;
658            
659 0           require Set::IntSpan;
660              
661 0           while (my ($k, $v) = each %groups) {
662 0 0         next unless @$v > 1;
663 0           my $union = Set::IntSpan->new;
664 0           my $min_pos;
665              
666 0           for my $vertex (@$v) {
667 0           my $label = $self->get_vertex_label($vertex);
668 0           $union->U($label->spans);
669 0   0       $min_pos //= $label->position;
670 0 0 0       $min_pos = $label->position if defined $label->position
671             and $label->position < $min_pos;
672             }
673              
674 0           my $class = Grammar::Formal::CharClass->new(
675             spans => $union,
676             position => $min_pos
677             );
678              
679 0           my $state = $self->fa_add_state(p => $class);
680              
681 0           $self->_copy_predecessors($v->[0], $state);
682 0           $self->_copy_successors($v->[0], $state);
683              
684 0           graph_delete_vertices_fast($self->g, @$v);
685             }
686             }
687              
688             #####################################################################
689             # Separate character classes
690             #####################################################################
691             sub fa_separate_character_classes {
692 0     0 1   my ($self) = @_;
693            
694 0           require Set::IntSpan::Partition;
695            
696             my @vertices = grep {
697 0           my $label = $self->get_vertex_label($_);
  0            
698 0 0         $label and $label->isa('Grammar::Formal::CharClass')
699             } $self->g->vertices;
700              
701             my @classes = map {
702 0           $self->get_vertex_label($_)->spans;
  0            
703             } @vertices;
704            
705 0           my %map = Set::IntSpan::Partition::intspan_partition_map(@classes);
706            
707 0           for (my $ix = 0; $ix < @vertices; ++$ix) {
708 0           for (@{ $map{$ix} }) {
  0            
709            
710 0           my $label = $self->get_vertex_label($vertices[$ix]);
711              
712 0           my $state = $self->fa_add_state(p =>
713             Grammar::Formal::CharClass->new(spans => $_,
714             position => $label->position));
715            
716 0           $self->_copy_predecessors($vertices[$ix], $state);
717 0           $self->_copy_successors($vertices[$ix], $state);
718             }
719            
720 0           graph_delete_vertex_fast($self->g, $vertices[$ix]);
721             }
722            
723             }
724              
725             #####################################################################
726             # ...
727             #####################################################################
728             sub _delete_not_allowed {
729 0     0     my ($self) = @_;
730 0           graph_delete_vertex_fast($self->g, $_) for grep {
731 0           my $label = $self->get_vertex_label($_);
732 0 0         $label and $label->isa('Grammar::Formal::NotAllowed');
733             } $self->g->vertices;
734             }
735              
736             #####################################################################
737             # ...
738             #####################################################################
739             sub _delete_unreachables {
740 0     0     my ($self) = @_;
741 0           my $symbols = $self->symbol_table;
742 0           my %keep;
743            
744 0           $keep{$_}++ for map {
745 0           my @suc = graph_all_successors_and_self($self->g, $_->{start_vertex});
746             # Always keep final vertices
747 0           my @fin = $_->{final_vertex};
748 0           (@suc, @fin);
749             } values %$symbols;
750              
751             graph_delete_vertices_fast($self->g, grep {
752 0           not $keep{$_}
  0            
753             } $self->g->vertices);
754             }
755              
756             #####################################################################
757             # Utils
758             #####################################################################
759             sub get_vertex_label {
760 0     0 0   my ($self, $v) = @_;
761 0           return $self->g->get_vertex_attribute($v, 'label');
762             }
763              
764             sub set_vertex_label {
765 0     0 0   my ($self, $v, $value) = @_;
766 0           $self->g->set_vertex_attribute($v, 'label', $value);
767             }
768              
769             sub vertex_isa {
770 0     0 0   my ($self, $v, $pkg) = @_;
771 0           return UNIVERSAL::isa($self->get_vertex_label($v), $pkg);
772             }
773              
774             sub vertex_partner {
775 0     0 0   my ($self, $v) = @_;
776 0           my $label = $self->get_vertex_label($v);
777 0 0         return unless $label;
778 0 0         return unless UNIVERSAL::can($label, 'partner');
779 0           return $label->partner;
780             }
781              
782             sub is_terminal_vertex {
783 0     0 0   my ($self, $v) = @_;
784 0 0         return unless $self->get_vertex_label($v);
785 0           return not $self->vertex_isa($v, 'Grammar::Formal::Empty');
786             }
787              
788             sub is_push_vertex {
789 0     0 0   my ($self, $v) = @_;
790 0   0       return $self->vertex_isa($v, 'Grammar::Graph::Start')
791             || $self->vertex_isa($v, 'Grammar::Graph::If');
792             }
793              
794             sub is_pop_vertex {
795 0     0 0   my ($self, $v) = @_;
796 0   0       return $self->vertex_isa($v, 'Grammar::Graph::Final')
797             || $self->vertex_isa($v, 'Grammar::Graph::Fi');
798             }
799              
800             sub is_matching_couple {
801 0     0 0   my ($self, $v1, $v2) = @_;
802 0           my $label = $self->get_vertex_label($v1);
803 0 0         return unless UNIVERSAL::can($label, 'partner');
804 0           return $label->partner eq $v2;
805             }
806              
807             #####################################################################
808             # Constructor
809             #####################################################################
810             sub _graph_copy_graph_without_terminal_out_edges {
811 0     0     my ($self) = @_;
812              
813 0           my $tmp = $self->g->copy;
814              
815 0           for my $v ($tmp->vertices) {
816 0 0         next unless $self->is_terminal_vertex($v);
817 0           for my $s ($tmp->successors($v)) {
818 0           $tmp->delete_edge($v, $s);
819             }
820             }
821              
822 0           return $tmp
823             }
824              
825             sub _create_vertex_to_topological {
826 0     0     my ($self) = @_;
827              
828 0           my $tmp = _graph_copy_graph_without_terminal_out_edges($self);
829              
830 0           my %result;
831              
832 0           my $ix = 1;
833 0           for my $scc ($tmp->strongly_connected_graph->toposort) {
834             # TODO: use get_graph_attribute subvertices instead of split
835 0           $result{$_} = $ix for split/\+/, $scc;
836 0           $ix++;
837             }
838              
839 0           return %result;
840             }
841              
842             sub _create_vertex_to_scc {
843 0     0     my ($self) = @_;
844              
845 0           my $tmp = _graph_copy_graph_without_terminal_out_edges($self);
846              
847 0           my %result;
848              
849 0           for my $scc ($tmp->strongly_connected_graph->toposort) {
850             # TODO: use get_graph_attribute subvertices instead of split
851 0 0 0       next unless $tmp->has_edge($scc, $scc) or $scc =~ /\+/;
852 0           $result{$_} = $scc for split/\+/, $scc;
853             }
854              
855 0           return %result;
856             }
857              
858             #####################################################################
859             # ...
860             #####################################################################
861              
862             sub fa_drop_rules_not_needed_for {
863 0     0 0   my ($self, $shortname) = @_;
864              
865 0           my $ref_graph = $self->_fa_ref_graph();
866 0           my $id = $self->_find_id_by_shortname($shortname);
867 0           my %keep = map { $_ => 1 } $id, $ref_graph->all_successors($id);
  0            
868              
869 0           delete $self->symbol_table->{$_} for grep {
870 0           not $keep{$_}
871 0           } keys %{ $self->symbol_table };
872             }
873              
874             #####################################################################
875             # ...
876             #####################################################################
877             sub fa_truncate {
878 0     0 0   my ($self) = @_;
879 0           graph_truncate_to_vertices_between($self->g,
880             $self->start_vertex, $self->final_vertex);
881             }
882              
883             #####################################################################
884             # Constructor
885             #####################################################################
886             sub from_grammar_formal {
887 0     0 1   my ($class, $formal, $shortname, %options) = @_;
888 0           my $self = $class->new;
889              
890 0           _add_to_automaton($formal, $self);
891 0           _delete_not_allowed($self);
892 0           fa_remove_useless_epsilons($self, $self->g->vertices);
893 0           _delete_unreachables($self);
894              
895 0           my $id = _find_id_by_shortname($self, $shortname);
896              
897 0           my ($start_vertex, $final_vertex) = _find_endpoints($self, $id);
898              
899 0           $self->_set_start_vertex($start_vertex);
900 0           $self->_set_final_vertex($final_vertex);
901              
902 0           $self->fa_prelude_postlude($shortname);
903              
904 0           return $self;
905             }
906              
907             #####################################################################
908             # Helper function to write some forms of repetition to the graph
909             #####################################################################
910             sub _bound_repetition {
911 0     0     my ($min, $max, $child, $fa, $root) = @_;
912              
913 0 0 0       die if defined $max and $min > $max;
914            
915 0 0 0       if ($min <= 1 and not defined $max) {
916 0           my $s1 = $fa->fa_add_state;
917 0           my $s2 = $fa->fa_add_state;
918 0           my $s3 = $fa->fa_add_state;
919 0           my $s4 = $fa->fa_add_state;
920 0           my ($ps, $pf) = _add_to_automaton($child, $fa, $root);
921 0           $fa->g->add_edge($s1, $s2);
922 0           $fa->g->add_edge($s2, $ps);
923 0           $fa->g->add_edge($pf, $s3);
924 0           $fa->g->add_edge($s3, $s4);
925 0 0         $fa->g->add_edge($s2, $s3) if $min == 0;
926 0           $fa->g->add_edge($s3, $s2); # loop
927 0           return ($s1, $s4);
928             }
929            
930 0           my $s1 = $fa->fa_add_state;
931 0           my $first = $s1;
932            
933 0           while ($min--) {
934 0           my ($src, $dst) = _add_to_automaton($child, $fa, $root);
935 0           $fa->g->add_edge($s1, $src);
936 0           $s1 = $dst;
937 0 0         $max-- if defined $max;
938             }
939              
940 0 0 0       if (defined $max and $max == 0) {
941 0           my $s2 = $fa->fa_add_state;
942 0           $fa->g->add_edge($s1, $s2);
943 0           return ($first, $s2);
944             }
945              
946 0   0       do {
947 0           my ($src, $dst) = _add_to_automaton($child, $fa, $root);
948 0           $fa->g->add_edge($s1, $src);
949 0           my $sx = $fa->fa_add_state;
950 0           $fa->g->add_edge($dst, $sx);
951 0           $fa->g->add_edge($s1, $sx); # optional because min <= 0 now
952 0 0         $fa->g->add_edge($sx, $s1) if not defined $max; # loop
953 0           $s1 = $sx;
954             } while (defined $max and --$max);
955              
956 0           my $s2 = $fa->fa_add_state;
957 0           $fa->g->add_edge($s1, $s2);
958              
959 0           return ($first, $s2);
960             }
961              
962             #####################################################################
963             # Collection of sub routines that write patterns to the graph
964             #####################################################################
965             sub convert_char_class {
966 0     0 0   my ($pattern, $fa, $root) = @_;
967 0           my $s1 = $fa->fa_add_state;
968 0           my $s2 = $fa->fa_add_state(p => $pattern);
969 0           my $s3 = $fa->fa_add_state;
970 0           $fa->g->add_edge($s1, $s2);
971 0           $fa->g->add_edge($s2, $s3);
972 0           return ($s1, $s3);
973             }
974              
975             sub convert_prose_value {
976 0     0 0   my ($pattern, $fa, $root) = @_;
977 0           my $s1 = $fa->fa_add_state;
978 0           my $s2 = $fa->fa_add_state(p => $pattern);
979 0           my $s3 = $fa->fa_add_state;
980 0           $fa->g->add_edge($s1, $s2);
981 0           $fa->g->add_edge($s2, $s3);
982 0           return ($s1, $s3);
983             }
984              
985             sub convert_reference {
986 0     0 0   my ($pattern, $fa, $root) = @_;
987 0           my $s1 = $fa->fa_add_state;
988 0           my $s2 = $fa->fa_add_state(p => $pattern);
989 0           my $s3 = $fa->fa_add_state;
990 0           $fa->g->add_edge($s1, $s2);
991 0           $fa->g->add_edge($s2, $s3);
992 0           return ($s1, $s3);
993             }
994              
995             sub convert_not_allowed {
996 0     0 0   my ($pattern, $fa, $root) = @_;
997 0           my $s1 = $fa->fa_add_state;
998 0           my $s2 = $fa->fa_add_state(p => $pattern);
999 0           my $s3 = $fa->fa_add_state;
1000 0           $fa->g->add_edge($s1, $s2);
1001 0           $fa->g->add_edge($s2, $s3);
1002 0           return ($s1, $s3);
1003             }
1004              
1005             sub convert_range {
1006 0     0 0   my ($pattern, $fa, $root) = @_;
1007 0           my $char_class = Grammar::Formal::CharClass
1008             ->from_numbers_pos($pattern->position, $pattern->min .. $pattern->max);
1009 0           return _add_to_automaton($char_class, $fa, $root);
1010             }
1011              
1012             sub convert_ascii_insensitive_string {
1013 0     0 0   my ($pattern, $fa, $root) = @_;
1014              
1015 1     1   10891 use bytes;
  1         2  
  1         7  
1016              
1017             my @spans = map {
1018 0           Grammar::Formal::CharClass
  0            
1019             ->from_numbers_pos($pattern->position, ord(lc), ord(uc))
1020             } split//, $pattern->value;
1021              
1022 0           my $group = Grammar::Formal::Empty->new;
1023              
1024 0           while (@spans) {
1025 0           $group = Grammar::Formal::Group->new(
1026             position => $pattern->position,
1027             p1 => pop(@spans),
1028             p2 => $group);
1029             }
1030              
1031 0           return _add_to_automaton($group, $fa, $root);
1032             }
1033              
1034             sub convert_case_sensitive_string {
1035 0     0 0   my ($pattern, $fa, $root) = @_;
1036              
1037             my @spans = map {
1038 0           Grammar::Formal::CharClass
  0            
1039             ->from_numbers_pos($pattern->position, ord)
1040             } split//, $pattern->value;
1041            
1042 0           my $group = Grammar::Formal::Empty->new;
1043              
1044 0           while (@spans) {
1045 0           $group = Grammar::Formal::Group->new(
1046             p1 => pop(@spans),
1047             p2 => $group
1048             );
1049             }
1050              
1051 0           return _add_to_automaton($group, $fa, $root);
1052             }
1053              
1054             sub convert_grammar {
1055 0     0 0   my ($pattern, $fa, $root) = @_;
1056            
1057             my %map = map {
1058 0           $_ => [ _add_to_automaton($pattern->rules->{$_}, $fa) ]
1059 0           } keys %{ $pattern->rules };
  0            
1060            
1061 0 0         return unless defined $pattern->start;
1062              
1063 0           my $s1 = $fa->fa_add_state;
1064 0           my $s2 = $fa->fa_add_state;
1065 0           my ($ps, $pf) = @{ $map{ $pattern->start } };
  0            
1066 0           $fa->g->add_edge($s1, $ps);
1067 0           $fa->g->add_edge($pf, $s2);
1068              
1069 0           return ($s1, $s2);
1070             }
1071              
1072             sub convert_grammar_formal {
1073 0     0 0   my ($pattern, $fa, $root) = @_;
1074            
1075             my %map = map {
1076 0           $_ => [ _add_to_automaton($pattern->rules->{$_}, $fa) ]
1077 0           } keys %{ $pattern->rules };
  0            
1078            
1079             # root, so we do not return src and dst
1080 0           return;
1081             }
1082              
1083             sub convert_rule {
1084 0     0 0   my ($pattern, $fa, $root) = @_;
1085 0           my $s1 = $fa->fa_add_state;
1086 0           my $s2 = $fa->fa_add_state;
1087              
1088 0           my $table = $fa->symbol_table;
1089              
1090             # FIXME(bh): error if already defined?
1091              
1092 0   0       $table->{$pattern} //= {};
1093 0           $table->{$pattern}{start_vertex} = $s1;
1094 0           $table->{$pattern}{final_vertex} = $s2;
1095 0           $table->{$pattern}{shortname} = $pattern->name;
1096              
1097 0           my $r1 = Grammar::Graph::Start->new(
1098             name => $pattern->name,
1099             partner => $s2,
1100             position => $pattern->position
1101             );
1102              
1103 0           my $r2 = Grammar::Graph::Final->new(
1104             name => $pattern->name,
1105             partner => $s1,
1106             position => $pattern->position
1107             );
1108              
1109 0           $fa->set_vertex_label($s1, $r1);
1110 0           $fa->set_vertex_label($s2, $r2);
1111            
1112 0           my ($ps, $pf) = _add_to_automaton(
1113             $pattern->p, $fa, [$pattern, $s1, $s2]);
1114            
1115 0           $fa->g->add_edge($s1, $ps);
1116 0           $fa->g->add_edge($pf, $s2);
1117            
1118 0           return ($s1, $s2);
1119             }
1120              
1121             sub convert_bounded_repetition {
1122 0     0 0   my ($pattern, $fa, $root) = @_;
1123 0           return _bound_repetition($pattern->min, $pattern->max, $pattern->p, $fa, $root);
1124             }
1125              
1126             sub convert_some_or_more {
1127 0     0 0   my ($pattern, $fa, $root) = @_;
1128 0           return _bound_repetition($pattern->min, undef, $pattern->p, $fa, $root);
1129             }
1130              
1131             sub convert_one_or_more {
1132 0     0 0   my ($self, $fa, $root) = @_;
1133 0           my $s1 = $fa->add_state;
1134 0           my $s2 = $fa->add_state;
1135 0           my $s3 = $fa->add_state;
1136 0           my $s4 = $fa->add_state;
1137 0           my ($ps, $pf) = $self->p->add_to_automaton($fa, $root);
1138 0           $fa->add_e_transition($s1, $s2);
1139 0           $fa->add_e_transition($s2, $ps);
1140 0           $fa->add_e_transition($pf, $s3);
1141 0           $fa->add_e_transition($s3, $s4);
1142 0           $fa->add_e_transition($s3, $s2);
1143            
1144 0           return ($s1, $s4);
1145             }
1146              
1147             sub convert_zero_or_more {
1148 0     0 0   my ($self, $fa, $root) = @_;
1149 0           my $s1 = $fa->add_state;
1150 0           my $s2 = $fa->add_state;
1151 0           my $s3 = $fa->add_state;
1152 0           my $s4 = $fa->add_state;
1153 0           my ($ps, $pf) = $self->p->add_to_automaton($fa, $root);
1154 0           $fa->add_e_transition($s1, $s2);
1155 0           $fa->add_e_transition($s2, $ps);
1156 0           $fa->add_e_transition($pf, $s3);
1157 0           $fa->add_e_transition($s3, $s4);
1158 0           $fa->add_e_transition($s3, $s2);
1159 0           $fa->add_e_transition($s2, $s3); # zero
1160            
1161 0           return ($s1, $s4);
1162             }
1163              
1164             sub convert_empty {
1165 0     0 0   my ($pattern, $fa, $root) = @_;
1166 0           my $s1 = $fa->fa_add_state;
1167 0           my $s3 = $fa->fa_add_state;
1168 0           my $s2 = $fa->fa_add_state;
1169 0           $fa->g->add_edge($s1, $s2);
1170 0           $fa->g->add_edge($s2, $s3);
1171 0           return ($s1, $s3);
1172             }
1173              
1174             sub convert_choice {
1175 0     0 0   my ($pattern, $fa, $root) = @_;
1176 0           my $s1 = $fa->fa_add_state;
1177 0           my $s2 = $fa->fa_add_state;
1178 0           my ($p1s, $p1f) = _add_to_automaton($pattern->p1, $fa, $root);
1179 0           my ($p2s, $p2f) = _add_to_automaton($pattern->p2, $fa, $root);
1180 0           $fa->g->add_edge($s1, $p1s);
1181 0           $fa->g->add_edge($s1, $p2s);
1182 0           $fa->g->add_edge($p1f, $s2);
1183 0           $fa->g->add_edge($p2f, $s2);
1184 0           return ($s1, $s2);
1185             }
1186              
1187             sub convert_group {
1188 0     0 0   my ($pattern, $fa, $root) = @_;
1189 0           my $s1 = $fa->fa_add_state;
1190 0           my $s2 = $fa->fa_add_state;
1191 0           my ($p1s, $p1f) = _add_to_automaton($pattern->p1, $fa, $root);
1192 0           my ($p2s, $p2f) = _add_to_automaton($pattern->p2, $fa, $root);
1193 0           $fa->g->add_edge($p1f, $p2s);
1194 0           $fa->g->add_edge($s1, $p1s);
1195 0           $fa->g->add_edge($p2f, $s2);
1196 0           return ($s1, $s2);
1197             }
1198              
1199             sub convert_conjunction {
1200 0     0 0   my ($pattern, $fa, $root) = @_;
1201              
1202 0           return _convert_binary_operation($pattern,
1203             $fa, $root, "conjunction");
1204             }
1205              
1206             sub convert_ordered_conjunction {
1207 0     0 0   my ($pattern, $fa, $root) = @_;
1208              
1209 0           return _convert_binary_operation($pattern,
1210             $fa, $root, "ordered_conjunction");
1211             }
1212              
1213             sub convert_ordered_choice {
1214 0     0 0   my ($pattern, $fa, $root) = @_;
1215              
1216 0           return _convert_binary_operation($pattern,
1217             $fa, $root, "ordered_choice");
1218             }
1219              
1220             sub _convert_binary_operation {
1221 0     0     my ($pattern, $fa, $root, $op) = @_;
1222 0           my $s1 = $fa->fa_add_state();
1223 0           my $s2 = $fa->fa_add_state();
1224 0           my $s3 = $fa->fa_add_state();
1225 0           my $s4 = $fa->fa_add_state();
1226              
1227 0           my $op1 = Grammar::Graph::Operand->new(
1228             position => $pattern->position, partner => $s3);
1229 0           my $op2 = Grammar::Graph::Operand->new(
1230             position => $pattern->position, partner => $s3);
1231 0           my $op3 = Grammar::Graph::Operand->new(
1232             position => $pattern->position, partner => $s4);
1233 0           my $op4 = Grammar::Graph::Operand->new(
1234             position => $pattern->position, partner => $s4);
1235              
1236 0           my $c1 = $fa->fa_add_state(p => $op1);
1237 0           my $c2 = $fa->fa_add_state(p => $op2);
1238 0           my $c3 = $fa->fa_add_state(p => $op3);
1239 0           my $c4 = $fa->fa_add_state(p => $op4);
1240            
1241 0           my ($p1s, $p1f) = _add_to_automaton($pattern->p1, $fa, $root);
1242 0           my ($p2s, $p2f) = _add_to_automaton($pattern->p2, $fa, $root);
1243              
1244 0           my $l3 = Grammar::Graph::If->new(
1245             position => $pattern->position,
1246             partner => $s4,
1247             p1 => $c1,
1248             p2 => $c2,
1249             name => $op
1250             );
1251              
1252 0           my $l4 = Grammar::Graph::Fi->new(
1253             position => $pattern->position,
1254             partner => $s3,
1255             p1 => $c3,
1256             p2 => $c4,
1257             name => $op
1258             );
1259              
1260 0           $fa->set_vertex_label($s3, $l3);
1261 0           $fa->set_vertex_label($s4, $l4);
1262              
1263 0           $fa->g->add_edge($c1, $p1s);
1264 0           $fa->g->add_edge($c2, $p2s);
1265 0           $fa->g->add_edge($p1f, $c3);
1266 0           $fa->g->add_edge($p2f, $c4);
1267              
1268 0           $fa->g->add_edge($s3, $c1);
1269 0           $fa->g->add_edge($s3, $c2);
1270 0           $fa->g->add_edge($c3, $s4);
1271 0           $fa->g->add_edge($c4, $s4);
1272              
1273 0           $fa->g->add_edge($s1, $s3);
1274 0           $fa->g->add_edge($s4, $s2);
1275            
1276 0           return ($s1, $s2);
1277             }
1278              
1279             sub convert_subtraction {
1280 0     0 0   my ($pattern, $fa, $root) = @_;
1281 0           return _convert_binary_operation($pattern, $fa, $root, "and_not");
1282             }
1283              
1284             sub _add_to_automaton {
1285 0     0     my ($pattern, $self, $root) = @_;
1286 0           my $converter = $self->find_converter(ref $pattern);
1287 0 0         if ($converter) {
1288 0           return $converter->($pattern, $self, $root);
1289             }
1290 0           my $s1 = $self->fa_add_state;
1291 0           my $s2 = $self->fa_add_state(p => $pattern);
1292 0           my $s3 = $self->fa_add_state;
1293 0           $self->g->add_edge($s1, $s2);
1294 0           $self->g->add_edge($s2, $s3);
1295 0           return ($s1, $s3);
1296             }
1297              
1298             1;
1299              
1300             __END__
1301              
1302             =head1 NAME
1303              
1304             Grammar::Graph - Graph representation of formal grammars
1305              
1306             =head1 SYNOPSIS
1307              
1308             use Grammar::Graph;
1309             my $g = Grammar::Graph->from_grammar_formal($formal);
1310             my $symbols = $g->symbol_table;
1311             my $new_state = $g->fa_add_state();
1312             ...
1313              
1314             =head1 DESCRIPTION
1315              
1316             Graph representation of formal grammars.
1317              
1318             =head1 METHODS
1319              
1320             =over
1321              
1322             =item C<from_grammar_formal($grammar_formal)>
1323              
1324             Constructs a new C<Grammar::Graph> object from a L<Grammar::Formal>
1325             object. C<Grammar::Graph> derives from L<Graph>. The graph has a
1326             graph attribute C<symbol_table> with an entry for each rule identifying
1327             C<start_vertex>, C<final_vertex>, C<shortname>, and other properties.
1328              
1329             =item C<fa_add_state(p => $label)>
1330              
1331             Adds a new vertex to the graph and optionally labeles it with the
1332             supplied label. The vertex should be assumed to be a random integer.
1333             Care should be taken when adding vertices to the graph through other
1334             means to avoid clashes.
1335              
1336             =item C<fa_all_e_reachable($v)>
1337              
1338             Returns the successors of $v and transitively any successors that can
1339             be reached without going over a vertex labeled by something other than
1340             C<Grammar::Formal::Empty>-derived objects. In other words, all the
1341             vertices that can be reached without going over an input symbol.
1342              
1343             =item C<fa_expand_references()>
1344              
1345             Modifies the graph such that vertices are no longer labeled with
1346             C<Grammar::Formal::Reference> nodes provided there is an entry for
1347             the referenced symbol in the Graph's C<symbol_table>. Recursive and
1348             cyclic references are linearised by vertices labeled with special
1349             C<Grammar::Graph::Start> and C<Grammar::Graph::Final> nodes, and
1350             they in turn are protected by C<Grammar::Graph::Prefix> and linked
1351             C<Grammar::Graph::Suffix> nodes (the former identify the rule, the
1352             latter identify the reference) to ensure the nesting relationship
1353             can be fully recovered.
1354              
1355             =item C<fa_merge_character_classes()>
1356              
1357             Vertices labeled with a C<Grammar::Formal::CharClass> node that share
1358             the same set of predecessors and successors are merged into a single
1359             vertex labeled with a C<Grammar::Formal::CharClass> node that is the
1360             union of original vertices.
1361              
1362             =item C<fa_separate_character_classes()>
1363              
1364             Collects all vertices labeled with a C<Grammar::Formal::CharClass> node
1365             in the graph and replaces them with vertices labeled with
1366             C<Grammar::Formal::CharClass> nodes such that an input symbol matches
1367             at most a single C<Grammar::Formal::CharClass>.
1368              
1369             =item C<fa_remove_useless_epsilons()>
1370              
1371             Removes vertices labeled with nothing or C<Grammar::Formal::Empty> nodes
1372             by connecting all predecessors to all successors directly. The check for
1373             C<Grammar::Formal::Empty> is exact, derived classes do not match.
1374              
1375             =back
1376              
1377             =head1 EXPORTS
1378              
1379             None.
1380              
1381             =head1 AUTHOR / COPYRIGHT / LICENSE
1382              
1383             Copyright (c) 2014-2017 Bjoern Hoehrmann <bjoern@hoehrmann.de>.
1384             This module is licensed under the same terms as Perl itself.
1385              
1386             =cut