/usr/local/CPAN/Games-Checkers/Games/Checkers/PDNParser.pm


# Games::Checkers, Copyright (C) 1996-2004 Mikhael Goikhman
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

use strict;

package Games::Checkers::PDNParser;

use Games::Checkers::LocationConversions;
use IO::File;

sub new ($$) {
	my $class = shift;
	my $fileName = shift;

	$fileName .= ".pdn.gz" if -r "$fileName.pdn.gz";
	$fileName .= ".pdn" if -r "$fileName.pdn";
	$fileName .= ".gz" if -r "$fileName.gz";
	my $fileToOpen = $fileName =~ /\.gz$/? "zcat $fileName |": $fileName;
	my $fd = new IO::File $fileToOpen;
	die "Can't open PDN for reading ($fileName)\n" unless $fd;

	my $self = { fn => $fileName, fd => $fd, lineno => 0 };
	bless $self, $class;
	return $self;
}

sub errorPrefix {
	my $self = shift;
	"Error parsing $self->{fn}, line $self->{lineno}, corrupted record:\n";
}

sub nextRecord ($) {
	my $self = shift;

	my $recordValues = {};

	my $line;
	my $notEnd = 0;
	while ($line = $self->{fd}->getline) {
		$self->{lineno}++;
		next if $line =~ /^\s*(([#;]|{.*}|\(.*\))\s*)?$/;
		$notEnd = 1;
		if ($line =~ /\[(\w+)\s+"?(.*?)"?\]/) {
			$recordValues->{$1} = $2;
			next;
		}
		last;
	}
	return undef unless $notEnd;

	my $result = $recordValues->{Result};
	die $self->errorPrefix . "\tNon empty named value 'Result' is missing\n"
		unless $result;
	my $lineno = $self->{lineno};

	my $moveString = "";
	while (!$moveString || ($line = $self->{fd}->getline) && $self->{lineno}++) {
		$line =~ s/[\r\n]+$/ /;
		$moveString .= $line;
		last if $line =~ /$result/;

		# tolerate some broken PDNs without trailing result separator
		my $nextChar = $self->{fd}->getc;
		$self->{fd}->ungetc(ord($nextChar));
		last if $nextChar eq "[";
	}

	# tolerate some broken PDNs without result separator
#	die $self->errorPrefix . "\tSeparator ($result) is not found from line $lineno\n"
#		unless $line;

	$moveString =~ s/\b$result\b.*//;
	$moveString =~ s/{[^}]*}//g;  # remove comments
	$moveString =~ s/\([^\)]*(\)[^(]*)?\)//g;  # remove comments
	$moveString =~ s/([x:*-])\s+(\d|\w)/$1$2/gi;  # remove alignment spaces
	my @moveVergeStrings = split(/(?:\s+|\d+\.\s*)+/, $moveString);
	shift @moveVergeStrings while @moveVergeStrings && !$moveVergeStrings[0];

	my @moveVergeTrios = map {
		/^((\d+)|\w\d)([x:*-])((\d+)|\w\d)$/i
			|| die $self->errorPrefix . "\tIncorrect move notation ($_)\n";
		[
			$3 eq "-"? 0: 1,
			defined $2? numToLocation($1): strToLocation($1),
			defined $5? numToLocation($4): strToLocation($4),
		]
	} @moveVergeStrings;

	return [ \@moveVergeTrios, $recordValues ];
}

1;