#!/afs/athena/contrib/perl/perl

sub setformat { select((select($_[0]),$~=$_[1])[0]); }

format extint =
extern int ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$varsh
           ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
$varsh

.
    ;

format int =
int ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$varsc
    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
$varsc

.
    ;

format column =
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$varsc
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
$varsc

.
    ;

sub cvt {
    local($file) = @_;
    local($eq,%neg,%var);

    $file =~ s/\.eqn$//;

    open(IN,"$file.eqn") || die "couldn't open $file.eqn: $!\n";
    open(OUTH,">$file.h") || die "couldn't open $file.h for writing: $!\n";
    open(OUTC,">$file.c") || die "couldn't open $file.c for writing: $!\n";

    while(<IN>) {
	next if /^$/ || /^device/ || /^enable/ || /^'/;
	$reg = s/register//;
	$neg = s/negative//;

	if (s/positive// || $reg || $neg) {
	    s/;//;
	    s/\s*//g;
	    for $s (split(/,/)) {
		($s =~ /@/) && ($s = $`);
		$s = $file.$s;
		$neg{$s}="(" if $neg;
		$nega{$s}="^1)" if $neg;
		$reg{$s}++ if $reg;
		$var{$s}++;
	    }
	} else {
	    $eq .= $_;
	    for $s (split(/\W+/)) {
		$var{$file.$s}++ if $s;
	    }
	    if ($eq =~ /;$/ && $eq =~ /^(\w+)/) {
		$s = $1;
		
		$eq =~ tr,+*,|&,;
		$eq =~ s/\w+/$file$&/g;
		$eq =~ s/(\w+)( [^=]|;)/$1temp$2/g;
		$eq =~ s,/(\w+),($1^1),g;

		$eq{$file.$s} = $eq;

		$eq = "";
	    }
	}
    }

    $varsh = $varsc = join(", ",sort keys %var).";";

    #### write the header file
    ## declare all the variables

    &setformat("OUTH","extint");
    write OUTH;

    ## declare the functions

    print OUTH "
void ${file}_comb(void);
void ${file}_reg(void);
int *${file}_conv(char *);
";
    close(OUTH);

    #### write the c file
    ## define all the variables

    &setformat("OUTC","int");
    write OUTC;

    ## define the string->var conversion array

    print OUTC "static struct vtbl { char *name; int *var; } ${file}_conv[] = {\n";
    for $var (sort keys %var) {
	print OUTC "\t\"$var\", \&$var,\n";
    }
    print OUTC "\t(char *)0,(int *)0\n};\n\n";

    ## define the conversion function

    print OUTC "
#include <string.h>

int *${file}_addr(char *name)
{
    struct vtbl *ptr;
    
    for (ptr=${file}_conv; ptr->name; ptr++)
    if (strcmp(ptr->name,name) == 0) return(ptr->var);
}

";#"
    ## define the combination function

    print OUTC "void ",$file,"_comb()\n{\n";
    $varsc = join(", ",grep($_=$_."temp = ".$neg{$_}.$_.$nega{$_},
			    sort keys %var)).";";
    &setformat("OUTC","int");
    write OUTC;

    for $var (sort keys %var) {
        next if $reg{$var};
	print OUTC $eq{$var},"\n";
    }

    $varsc = join("; ", grep(!$reg{$_} && $eq{$_} && ($_=$_." = (".$_."^1)"),
			      sort keys %neg)).";";
    &setformat("OUTC","column");
    write OUTC;

    print OUTC "}\n";

    ## define the register function

    print OUTC "\nvoid ",$file,"_reg()\n{\n";
    $varsc = join(", ",grep($_=$_."temp = ".$neg{$_}.$_.$nega{$_},
			    sort keys %var)).";";
    &setformat("OUTC","int");
    write OUTC;

    for $var (sort keys %var) {
	next if !$reg{$var};
	print OUTC $eq{$var},"\n";
    }

    $varsc = join("; ", grep($reg{$_} && $eq{$_} && ($_=$_." = (".$_."^1)"),
			      sort keys %neg)).";";
    &setformat("OUTC","column");
    write OUTC;

    print OUTC "}\n";

    close(OUTC);
    close(IN);
}

while ($file = shift(@ARGV)) {
    &cvt($file);
}
