#############################################################################
# Parse text definition into a Graph::Simple object
#
# (c) by Tels 2004 - 2005.
#############################################################################

package Graph::Simple::Parser;

use 5.006001;
use strict;
use warnings;
use Graph::Simple;

use vars qw/$VERSION/;

$VERSION = '0.04';

sub new
  {
  my $class = shift;

  my $self = bless {}, $class;

  my $args = $_[0];
  $args = { @_ } if ref($args) ne 'HASH';

  $self->_init($args);
  }

sub _init
  {
  my ($self,$args) = @_;

  $self->{error} = '';
  
  foreach my $k (keys %$args)
    {
#    if ($k !~ /^(|debug)\z/)
#      {
#      $self->error ("Unknown option '$k'");
#      }
    $self->{$k} = $args->{$k};
    }

  $self;
  }

sub from_text
  {
  my ($self,$txt) = @_;

  my $graph = Graph::Simple->new( { debug => $self->{debug} } );

  return $graph if !defined $txt || $txt =~ /^\s*\z/;		# empty text?

  my @lines = split /\n/, $txt;

  my $c = 'Graph::Simple::Node';
  my $e = 'Graph::Simple::Edge';
  my $nr = -1;
  LINE:
  foreach my $line (@lines)
    {
    $nr++;
    # remove comment:
#    $line =~ s/#.*//;

    chomp($line);

    # remove white space at start/end
    $line =~ s/^\s+//;
    $line =~ s/\s+\z//;

#    print STDERR "at line $nr '$line'\n";

    # [ Berlin ]
    if ($line =~ /^\[\s*([^\]]+?)\s*\]\z/)
      {
      my $n1 = $1;
      my $node_a = $graph->node($n1);
      if (!defined $node_a)
        {
        $node_a = $c->new( { name => $n1 } ); 
        $graph->add_node ( $node_a );
        }
      next LINE;
      }

    # [ Berlin ] -> [ Kassel ]
    #                      1              2    3     4                 6
    if ($line =~ /^\[\s*([^\]]+?)\s*\]\s*(<?)((=|-|- |\.)+)(>?)\s*\[\s*([^\]]+?)\s*\]/)
      {
      my $n1 = $1; my $n6 = $6; my $n3 = $3;
      my $node_a = $graph->node($n1);
      my $node_b = $graph->node($n6);
      if (!defined $node_a || !defined $node_b)
        {
        $node_a = $c->new( { name => $n1 } ) unless defined $node_a;
        $node_b = $c->new( { name => $n6 } ) unless defined $node_b;
        my $style = '--';	# default
#        print STDERR "edge style '$n3'\n";
        $style = '==' if $n3 =~ /^=+\z/; 
        $style = '..' if $n3 =~ /^\.+\z/; 
        $style = '- ' if $n3 =~ /^(- )+\z/; 
#        print STDERR "edge style '$style'\n";
        # XXX TODO: look at $n2 and $n4 for left/right direction
        my $edge = $e->new( { style => $style . '>' } );
        $graph->add_edge ( $node_a, $node_b, $edge ); 
        }
      next LINE;
      }

    }

  $graph;
  }

sub error
  {
  my $self = shift;

  $self->{error} = $_[0] if defined $_[0];
  $self->{error};
  }

1;
__END__

=head1 NAME

Graph::Simple::Parser - Parse graph from textual description

=head1 SYNOPSIS

        # creating a graph from a textual description
        use Graph::Simple::Parser;
        my $parser = Graph::Simple::Parser->new();

        my $graph = $parser->from_text(
                '[ Bonn ] => [ Berlin ]'.
                '[ Berlin ] => [ Rostock ]'.
        );
        print $graph->as_ascii( );

=head1 DESCRIPTION

C<Graph::Simple::Parser> lets you parse simple textual descriptions
of graphs, and constructs a C<Graph::Simple> object from them.

The resulting object can than be used to layout and output the graph.

=head2 Input

The input consists of text describing the graph.

	[ Bonn ]      --> [ Berlin ]
	[ Frankfurt ] <=> [ Dresden ]
	[ Bonn ]      --> [ Frankfurt ]
	[ Bonn ]      ==> [ Frankfurt ]

See L<Output> for how this will be rendered in ASCII art.

The edges between the nodes can have the following styles:

	-->		line
	==>		double line
	..>		dotted
	- >		dashed

In additon the following three directions are possible:

	 -->		connect the node on the left to the node on the right
	<-->		the direction between the nodes
			goes into both directions at once
	<--		connect the node on the right to the node on the left

Of course you can combine all three directions with all styles.

=head2 Output

The output will be a L<Graph::Simple> object, see there for what you
can do with it.

=head1 EXAMPLES

See L<Graph::Simple> for an extensive list of examples.

=head1 METHODS

C<Graph::Simple::Parser> supports the following methods:

=head2 new()

	use Graph::Simple::Parser;
	my $parser = Graph::Simple::Parser->new();

Creates a new parser object.

=head2 from_text()

	my $graph = $parser->from_text();

Create a L<Graph::Simple> object. Returns undef for error, you
can find the error with L<error()>.

This method will reset any previous error, and thus the C<$parser> object
can be re-used to parse different texts.

=head2 error()

	my $error = $parser->error();

Returns the last error.

=head1 EXPORT

Exports nothing.

=head1 SEE ALSO

L<Graph::Simple>.

=head1 AUTHOR

Copyright (C) 2004 - 2005 by Tels L<http://bloodgate.com>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms of the GPL. See the LICENSE file for information.

=cut
