Grammar-Graph

 view release on metacpan or  search on metacpan

lib/Grammar/Graph.pm  view on Meta::CPAN

    'Grammar::Formal::OneOrMore' => \&convert_one_or_more,
    'Grammar::Formal::ZeroOrMore' => \&convert_zero_or_more,

    'Grammar::Formal::Empty' => \&convert_empty,

    'Grammar::Formal::Group' => \&convert_group,

    'Grammar::Formal::Choice' => \&convert_choice,
    'Grammar::Formal::Conjunction' => \&convert_conjunction,
    'Grammar::Formal::Subtraction' => \&convert_subtraction,

    'Grammar::Formal::OrderedChoice' => \&convert_ordered_choice,
    'Grammar::Formal::OrderedConjunction'
      => \&convert_ordered_conjunction,

  } },
);

sub reversed_copy {
  my ($self) = @_;

  my $g = Graph::Directed->new;

  $g->add_edge(reverse @$_) for $self->g->edges;

  my $copy = $self->new(%{ $self }, g => $g);

  for my $v ($self->g->vertices) {
    my $label = $self->get_vertex_label($v);
    next unless $label;
    if (0 && UNIVERSAL::can($label, 'partner')) {
      my $cloned = $label->new(%$label, partner => $v);
      $copy->set_vertex_label($label->partner, $cloned);
    } else {
      my $cloned = $label->new(%$label);
      $copy->set_vertex_label($v, $cloned);
    }
  }

  $copy->_set_start_vertex($self->final_vertex);
  $copy->_set_final_vertex($self->start_vertex);

  return $copy;
}

#####################################################################
# Helper functions
#####################################################################
sub _copy_predecessors {
  my ($self, $src, $dst) = @_;
  $self->g->add_edge($_, $dst)
    for $self->g->predecessors($src);
}

sub _copy_successors {
  my ($self, $src, $dst) = @_;
  $self->g->add_edge($dst, $_)
    for $self->g->successors($src);
}

sub _find_endpoints {
  my ($self, $id) = @_;

  my $symbols = $self->symbol_table;
  my $start = $symbols->{$id}{start_vertex};
  my $final = $symbols->{$id}{final_vertex};
  
  return ($start, $final);
}

#####################################################################
# ...
#####################################################################

sub register_converter {
  my ($self, $class, $code) = @_;
  $self->pattern_converters->{$class} = $code;
}

sub find_converter { 
  my ($self, $pkg) = @_;
  return $self->pattern_converters->{$pkg};
}

#####################################################################
# ...
#####################################################################

sub _fa_next_id {
  my ($self) = @_;
  
  my $next_id = $self->g->get_graph_attribute('fa_next_id');
  
  $next_id = do {
    my $max = max(grep { /^[0-9]+$/ } $self->g->vertices) // 0;
    $max + 1;
  } if not defined $next_id or $self->g->has_vertex($next_id);

  $self->g->set_graph_attribute('fa_next_id', $next_id + 1);

  return $next_id;
}

sub fa_add_state {
  my ($self, %o) = @_;
  
  my $expect = $o{p} // Grammar::Formal::Empty->new;
  
  my $id = $self->_fa_next_id();
  $self->g->add_vertex($id);
  $self->set_vertex_label($id, $expect)
    if defined $expect;

  return $id;
}

sub fa_all_e_reachable {
  my ($self, $v) = @_;
  my %seen;
  my @todo = ($v);
  while (@todo) {

lib/Grammar/Graph.pm  view on Meta::CPAN

  my ($self, $id) = @_;

  my %id_to_refs = partition_by {
    $self->get_vertex_label($_)->expand . ''
  } grep {
    $self->vertex_isa($_, 'Grammar::Formal::Reference')
  } $self->g->vertices;

  for my $v (@{ $id_to_refs{$id} }) {
    my $label = $self->get_vertex_label($v);

    my ($src, $dst) = $self->_clone_non_terminal($id);

    $self->_copy_predecessors($v, $src);
    $self->_copy_successors($v, $dst);
    graph_delete_vertex_fast($self->g, $v);
  }
}

sub fa_expand_references {
  my ($self) = @_;
  my $symbols = $self->symbol_table;

  my $ref_graph = $self->_fa_ref_graph;
  my $scg = $ref_graph->strongly_connected_graph;

  my @topo = grep { not $ref_graph->has_edge($_, $_) }
    reverse $scg->toposort;

  for my $id (@topo) {
    # NOTE: Relies on @topo containing invalid a+b+c+... IDs
    $self->fa_expand_one_by_copying($id);
  }

  for my $v ($self->g->vertices) {
    my $label = $self->get_vertex_label($v);

    next unless $self->vertex_isa($v, 'Grammar::Formal::Reference');

    my $id = $label->expand;

    # TODO: explain
    # TODO: remove
#    next if $scg->has_vertex("$id")
#      && !$ref_graph->has_edge("$id", "$id");

    my $v1 = $self->fa_add_state();
    my $v2 = $self->fa_add_state();

    my $name = $label->expand->name;

    my $p1 = Grammar::Graph::Start->new(
      partner => $v2, name => $name);
      
    my $p2 = Grammar::Graph::Final->new(
      partner => $v1, name => $name);

    $self->set_vertex_label($v1, $p1);
    $self->set_vertex_label($v2, $p2);

    my ($start, $final) = $self->_find_endpoints($id);

    $self->_copy_predecessors($v, $v1);
    $self->_copy_successors($start, $v1);

    $self->_copy_successors($v, $v2);
    $self->_copy_predecessors($final, $v2);
    
    graph_delete_vertex_fast($self->g, $v);
  }

  for my $v ($self->g->vertices) {
    die if $self->vertex_isa($v, 'Grammar::Formal::Reference');
  }

}

#####################################################################
# Encapsulate ...
#####################################################################

sub _find_id_by_shortname {
  my ($self, $shortname) = @_;

  for my $k (keys %{ $self->symbol_table }) {
    next unless $self->symbol_table->{$k}{shortname} eq $shortname;
    return $k;
  }
}

sub fa_prelude_postlude {
  my ($self, $shortname) = @_;

  my $s1 = $self->fa_add_state();
  my $s2 = $self->fa_add_state();

  my $sS = $self->fa_add_state();
  my $sF = $self->fa_add_state();

  my $p1 = Grammar::Graph::Prelude->new(partner => $s2);
  my $p2 = Grammar::Graph::Postlude->new(partner => $s1);

  my $pS = Grammar::Graph::Start->new(name => "", partner => $sF);
  my $pF = Grammar::Graph::Final->new(name => "", partner => $sS);

  $self->set_vertex_label($s1, $p1);
  $self->set_vertex_label($s2, $p2);

  $self->set_vertex_label($sS, $pS);
  $self->set_vertex_label($sF, $pF);

  my $id = _find_id_by_shortname($self, $shortname);

  die unless defined $id;

  my $rd = $self->symbol_table->{$id};

=pod

  _copy_predecessors($self, $rd->{start_vertex}, $s1);
  _copy_successors($self, $rd->{start_vertex}, $s1);

lib/Grammar/Graph.pm  view on Meta::CPAN

  }

  return %result;
}

sub _create_vertex_to_scc {
  my ($self) = @_;

  my $tmp = _graph_copy_graph_without_terminal_out_edges($self);

  my %result;

  for my $scc ($tmp->strongly_connected_graph->toposort) {
    # TODO: use get_graph_attribute subvertices instead of split
    next unless $tmp->has_edge($scc, $scc) or $scc =~ /\+/;
    $result{$_} = $scc for split/\+/, $scc;
  }

  return %result;
}

#####################################################################
# ...
#####################################################################

sub fa_drop_rules_not_needed_for {
  my ($self, $shortname) = @_;

  my $ref_graph = $self->_fa_ref_graph();
  my $id = $self->_find_id_by_shortname($shortname);
  my %keep = map { $_ => 1 } $id, $ref_graph->all_successors($id);

  delete $self->symbol_table->{$_} for grep {
    not $keep{$_}
  } keys %{ $self->symbol_table };
}

#####################################################################
# ...
#####################################################################
sub fa_truncate {
  my ($self) = @_;
  graph_truncate_to_vertices_between($self->g,
    $self->start_vertex, $self->final_vertex);
}

#####################################################################
# Constructor
#####################################################################
sub from_grammar_formal {
  my ($class, $formal, $shortname, %options) = @_;
  my $self = $class->new;

  _add_to_automaton($formal, $self);
  _delete_not_allowed($self);
  fa_remove_useless_epsilons($self, $self->g->vertices);
  _delete_unreachables($self);

  my $id = _find_id_by_shortname($self, $shortname);

  my ($start_vertex, $final_vertex) = _find_endpoints($self, $id);

  $self->_set_start_vertex($start_vertex);
  $self->_set_final_vertex($final_vertex);

  $self->fa_prelude_postlude($shortname);

  return $self;
}

#####################################################################
# Helper function to write some forms of repetition to the graph
#####################################################################
sub _bound_repetition {
  my ($min, $max, $child, $fa, $root) = @_;

  die if defined $max and $min > $max;
  
  if ($min <= 1 and not defined $max) {
    my $s1 = $fa->fa_add_state;
    my $s2 = $fa->fa_add_state;
    my $s3 = $fa->fa_add_state;
    my $s4 = $fa->fa_add_state;
    my ($ps, $pf) = _add_to_automaton($child, $fa, $root);
    $fa->g->add_edge($s1, $s2);
    $fa->g->add_edge($s2, $ps);
    $fa->g->add_edge($pf, $s3);
    $fa->g->add_edge($s3, $s4);
    $fa->g->add_edge($s2, $s3) if $min == 0;
    $fa->g->add_edge($s3, $s2); # loop
    return ($s1, $s4);
  }
  
  my $s1 = $fa->fa_add_state;
  my $first = $s1;
  
  while ($min--) {
    my ($src, $dst) = _add_to_automaton($child, $fa, $root);
    $fa->g->add_edge($s1, $src);
    $s1 = $dst;
    $max-- if defined $max;
  }

  if (defined $max and $max == 0) {
    my $s2 = $fa->fa_add_state;
    $fa->g->add_edge($s1, $s2);
    return ($first, $s2);
  }  

  do {
    my ($src, $dst) = _add_to_automaton($child, $fa, $root);
    $fa->g->add_edge($s1, $src);
    my $sx = $fa->fa_add_state;
    $fa->g->add_edge($dst, $sx);
    $fa->g->add_edge($s1, $sx); # optional because min <= 0 now
    $fa->g->add_edge($sx, $s1) if not defined $max; # loop
    $s1 = $sx;
  } while (defined $max and --$max);

  my $s2 = $fa->fa_add_state;
  $fa->g->add_edge($s1, $s2);



( run in 1.644 second using v1.01-cache-2.11-cpan-39bf76dae61 )