#!/usr/bin/perl
#
########################################################################
#
# Verwendung:
# goto [-s] [-f eingabefile] program [V1] [V2] ...
# 
# "program" ist ein File, das ein GOTO-Prgramm enthält
# 
# Die optinalen Argumente V1, ... sind die Anfangswerte für die Variablen.
#
# Optionen:
#
# -s : Ausführung erfolgt schrittweise
#
# -f eingabefile : Anfangswert der Variablen werden aus dem "eingabefile" 
#                  eingelesen. 
#
############################# Ende Verwendung ###########################

use Getopt::Std;

getopts('sf:',\%option);

# Variablen beginnen mit Index 1
$V[0] = 0;            

# Lies Anfangswerte aus File
if ( $option{f} ) {
    open(INPUT,$option{f});
    while ( <INPUT> ) {
	while ( /(\d+)/g ) {
	    push(@V,$1);
	}
    }
}
	    
# Lies Programmname und Anfangswerte aus der Kommandozeile
foreach ( @ARGV ) {
    if ( /^\d+$/ ) {
	if ( !$option{f} ) {
	    push(@V,$_); 
	}
    } else {
	$prog_name = $_;
    }
}


# Parse Programm
open(PROG,$prog_name);

while( <PROG> ) {
    if ( /^\s*(\d+)\s*IF\s*V(\d+)\s*=\s*V(\d+)\s*GOTO\s*(\d+)/i ) {
	# IF ... GOTO
	$command[$1] = { name => "j", p1 => $2, p2 => $3, p3 => $4};
	$max_line = ($1 < $max_line)?$max_line:$1;
	$max_var = ($2 < $max_var)?$max_var:$2;
	$max_var = ($3 < $max_var)?$max_var:$3;
    } elsif ( /^\s*(\d+)\s*GOTO\s*(\d+)/i ) {
	# GOTO
	$command[$1] = { name => "g", p1 => $2 };
	$max_line = ($1 < $max_line)?$max_line:$1;
    } elsif ( /^\s*(\d+)\s*V(\d+)\s*(?:<-|:=)\s*0/i ) {
	# Vi <- 0
	$command[$1] = { name => "z", p1 => $2 };
	$max_line = ($1 < $max_line)?$max_line:$1;
	$max_var = ($2 < $max_var)?$max_var:$2;
    } elsif ( /^\s*(\d+)\s*V(\d+)\s*(?:<-|:=)\s*V\d+\s*\+\s*1/i ) {
	# Vi <- Vi + 1
	$command[$1] = { name => "s", p1 => $2 };
	$max_line = ($1 < $max_line)?$max_line:$1;
	$max_var = ($2 < $max_var)?$max_var:$2;
    } elsif ( /^\s*(\d+)\s*V(\d+)\s*(?:<-|:=)\s*V(\d+)/i ) {
	# Vi <- Vj
	$command[$1] = { name => "t", p1 => $3, p2 => $2 };
	$max_line = ($1 < $max_line)?$max_line:$1;
	$max_var = ($2 < $max_var)?$max_var:$2;
	$max_var = ($3 < $max_var)?$max_var:$3;
    } else {
	# Fehlerhafte Zeilen werden übersprungen, aber angezeigt
	print "Syntax error after line $max_line.\n";
    }
}

# Programmzeile, am Anfang 1
$line = 1;
# Zählung der Schritte beginnt mit 1
$step = 1;

# Tabelle intialsieren
print_head();
print_config();

system("stty -echo");
# Programm ausführen
while ( $line <= $max_line && $line >= 1 ) {
    if ( $option{s} ) {
	# auf Eingabe warten
	$i = <STDIN>;
    }
    next_config();
    print_config();
}
# fertig
system("stty echo");
print "\n";

# Ein Schritt
sub next_config { 
    my $comm = $command[$line];
    $step++;
    if ( $comm->{name} eq "j" ) {
	if ( $V[$comm->{p1}] == $V[$comm->{p2}] ) {
	    $line = $comm->{p3};
	} else {
	    $line++;
	}
    } elsif ( $comm->{name} eq "g" ) {
	$line = $comm->{p1};
    } elsif ( $comm->{name} eq "z" ) {
	$V[$comm->{p1}] = 0;
	$line++;
    } elsif ( $comm->{name} eq "s" ) {
	$V[$comm->{p1}]++;
	$line++;
    } elsif ( $comm->{name} eq "t" ) {
	$V[$comm->{p2}] = $V[$comm->{p1}];
	$line++;
    }
}
 
# Zeile drucken   
sub print_config {
    printf("%4d. | %3d |",$step,$line);
    for ( $i = 1; $i <= $max_var; $i++ ) {
	printf("|%5d ",$V[$i]);
    }
    print "|\n";
}

# Tabellenkopf drucken
sub print_head {
    print "\n";
    print "      |   Z |";
    for ( $i = 1; $i <= $max_var; $i++ ) {
	printf("|  V%-3d",$i);
    }
    print "|\n";
    print "-"x(14+$max_var*7)."\n";
}


