#!/usr/bin/perl -w

#Compiler for CAMSI's R3K assembler.
#Copyright (C) 2005  Cedric VINCENT <cedric.vincent@gmail.com>

#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.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

use strict;
use Getopt::Std;
use locale;

main();

my %registres;
my @free_reg;
my $num_allocated_register;

my $num_while;
my $num_if;
my $num_for;

my $verbose;
my $debug;
my $strip;

my @context;

sub get_register
{
    my ($variable, $declaration) = @_;
    my $register = 0;
    my $i = 0;

    if( defined( $registres{"$variable"} )) {
	$register = $registres{"$variable"};
    } elsif( 0 == $declaration ) {
	printf(STDERR "use of undeclared variable '$variable' ");
	return -1;
    } elsif( $num_allocated_register <= 31 ) {
	for( $i = 0; $i <= 31 ; $i++) {
	    if( 0 == $free_reg[$i] ) {
		$register = $i + 1;
		$free_reg[$i] = 1;
		last;
	    }
	}	
	$registres{"$variable"} = $register;
	$num_allocated_register = $num_allocated_register + 1;
	if( 1 == $verbose ) {
	    printf("register R$register dedicated to variable '$variable'\n");
	}
    } else {
	printf(STDERR "no more free register (use 'free' instruction to desallocated unused varaiables) ");
	return -1;
    }
    
    return $register;
}

sub free_register
{
    my ($variable) = @_;
    my $register = 0;

    if( defined( $registres{"$variable"} )) {
	$register = $registres{"$variable"};
	delete( $registres{"$variable"} );
	$free_reg[$register - 1] = 0;
	if( 1 == $verbose ) {
	    printf("register R$register freed from variable '$variable'\n");
	}
    } else {
	printf(STDERR "'$variable' is not a valid register ");
	return -1;
    }
    
    return 0;
}

sub handle_copy
{
    my ($result, $right_operand) = @_;
    my $reg_result = 0;
    my $reg_right_operand = 0;

    $reg_result = get_register($result, 1);
    if(-1 == $reg_result) {
	return -1;
    }
    if( $right_operand =~ m/^-?\d+$/ ) {
	printf(DEST_FILE "addi R$reg_result,R0,$right_operand\n");
    } else {
	$reg_right_operand = get_register($right_operand, 0);
	if(-1 == $reg_right_operand) {
	    return -1;
	}
	printf(DEST_FILE "add R$reg_result,R0,R$reg_right_operand\n");
    }
        
    return 0;
}

sub handle_nor
{
    my ($result, $left_operand, $right_operand) = @_;
    my $reg_result = 0;
    my $reg_left_operand = 0;
    my $reg_right_operand = 0;

    $reg_result = get_register($result, 0);
    if(-1 == $reg_result) {
	return -1;
    }
    $reg_left_operand = get_register($left_operand, 0);
    if(-1 == $reg_left_operand) {
	return -1;
    }
    $reg_right_operand = get_register($right_operand, 0);
    if(-1 == $reg_right_operand) {
	return -1;
    }
    printf(DEST_FILE "nor R$reg_result,R$reg_left_operand,R$reg_right_operand\n");

    return 0;
}

sub handle_rimm
{
    my ($result, $left_operand, $right_operand, $op) = @_;
    my $reg_result = 0;
    my $reg_left_operand = 0;
    my $reg_right_operand = 0;

    $reg_result = get_register($result, 0);
    if(-1 == $reg_result) {
	return -1;
    }

    if( $left_operand =~ m/^-?\d+$/ ) {
	if( $right_operand =~ m/^-?\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_right_operand = get_register($right_operand, 0);
	    if(-1 == $reg_right_operand) {
		return -1;
	    }

	    if("sub" eq $op) { #subi n'existe pas...
	      printf(DEST_FILE "addi R$reg_result,R$reg_right_operand,-$left_operand\n");
	    }
	    else {
		printf(DEST_FILE $op."i R$reg_result,R$reg_right_operand,$left_operand\n");
	    }

	    if("sub" eq $op) { #inverse le r�ulat en cas de besoin
		printf(DEST_FILE "sub R$reg_result,R0,R$reg_result\n");
	    }
	    if("slt" eq $op) { #inverse le r�ulat en cas de besoin
		printf(DEST_FILE "xori R$reg_result,R$reg_result,1\n");
	    }
	    return 0;
	}
    }

    if( $right_operand =~ m/^-?\d+$/ ) {
	if( $left_operand =~ m/^-?\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_left_operand = get_register($left_operand, 0);
	    if(-1 == $reg_left_operand) {
		return -1;
	    }

	    if("sub" eq $op) {
	      printf(DEST_FILE "addi R$reg_result,R$reg_left_operand,-$right_operand\n");
	    }
	    else {
		printf(DEST_FILE $op."i R$reg_result,R$reg_left_operand,$right_operand\n");
	    }

	    return 0;
	}
    }

    $reg_left_operand = get_register($left_operand, 0);
    if(-1 == $reg_left_operand) {
	return -1;
    }

    $reg_right_operand = get_register($right_operand, 0);
    if(-1 == $reg_right_operand) {
	return -1;
    }

    printf(DEST_FILE "$op R$reg_result,R$reg_left_operand,R$reg_right_operand\n");

    return 0;
}

sub handle_shift
{
    my ($result, $left_operand, $right_operand, $op) = @_;
    my $reg_result = 0;
    my $reg_left_operand = 0;
    my $reg_right_operand = 0;

    $reg_result = get_register($result, 0);
    if(-1 == $reg_result) {
	return -1;
    }

    if( $left_operand =~ m/^-?\d+$/ ) {
	if( $right_operand =~ m/^-?\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_right_operand = get_register($right_operand, 0);
	    if(-1 == $reg_right_operand) {
		return -1;
	    }
	    printf(DEST_FILE "$op R$reg_result,R$reg_right_operand,$left_operand\n");
	    return 0;
	}
    }

    if( $right_operand =~ m/^-?\d+$/ ) {
	if( $left_operand =~ m/^-?\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_left_operand = get_register($left_operand, 0);
	    if(-1 == $reg_left_operand) {
		return -1;
	    }
	    printf(DEST_FILE "$op R$reg_result,R$reg_left_operand,$right_operand\n");
	    return 0;
	}
    }

    printf(STDERR "syntax error ");
    return -1;
}

sub handle_operation
{
    my ($operator, $result, $left_operand, $right_operand) = @_;
 
    my $ret_code = 0;

    if( $result =~ m/^-?\d+$/ ) {
	printf(STDERR "invalid result operand ");
	return -1;
    }
    
    if( $operator eq "+" ) {
	$ret_code = handle_rimm($result, $left_operand, $right_operand, "add");
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator eq "-" ) {
	$ret_code = handle_rimm($result, $left_operand, $right_operand, "sub");
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator eq "&" ) {
	$ret_code = handle_rimm($result, $left_operand, $right_operand, "and");
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator eq "|" ) {
	$ret_code = handle_rimm($result, $left_operand, $right_operand, "or");
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator eq "<<" ) {
	$ret_code = handle_shift($result, $left_operand, $right_operand, "lsl");
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator eq ">>" ) {
	$ret_code = handle_shift($result, $left_operand, $right_operand, "lsr");
	if(-1 == $ret_code) {
	    return -1;
	}
    }  elsif( $operator eq "<|" ) {
	$ret_code = handle_rimm($result, $left_operand, $right_operand, "slt");
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator eq "=" ) {
	$ret_code = handle_copy($result, $right_operand);
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator =~ m/nor/i ) {
	$ret_code = handle_nor($result, $left_operand, $right_operand);
	if(-1 == $ret_code) {
	    return -1;
	}
    } elsif( $operator =~ m/xor/i ) {
	$ret_code = handle_rimm($result, $left_operand, $right_operand, "xor");
	if(-1 == $ret_code) {
	    return -1;
	}
    } else {
	printf(STDERR "unknown operator '$operator' ");
	return -1;
    }

    return 0;
}

sub handle_mem
{
    my ($result, $left_operand, $right_operand, $op) = @_;
    my $reg_result = 0;
    my $reg_left_operand = 0;
    my $reg_right_operand = 0;

    $reg_result = get_register($result, 0);
    if(-1 == $reg_result) {
	return -1;
    }

    if( $left_operand =~ m/^\d+$/ ) {
	if( $right_operand =~ m/^\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_right_operand = get_register($right_operand, 0);
	    if(-1 == $reg_right_operand) {
		return -1;
	    }
	    printf(DEST_FILE "$op R$reg_result,$left_operand(R$reg_right_operand)\n");
	    return 0;
	}
    }

    if( $right_operand =~ m/^\d+$/ ) {
	if( $left_operand =~ m/\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_left_operand = get_register($left_operand, 0);
	    if(-1 == $reg_left_operand) {
		return -1;
	    }
	    printf(DEST_FILE "$op R$reg_result,$right_operand(R$reg_left_operand)\n");
	    return 0;
	}
    }

    printf(STDERR "syntax error ");
    return -1;
}

sub handle_eq
{
    my ($left_operand, $right_operand, $label, $comparator) = @_;
    my $reg_left_operand = 0;
    my $reg_right_operand = 0;
    my $ret_code = 0;

    if( $left_operand =~ m/^-?\d+$/ ) {
	if( $right_operand =~ m/^-?\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_right_operand = get_register($right_operand, 0);
	    if(-1 == $reg_right_operand) {
		return -1;
	    }

	    $reg_left_operand = get_register("00temp", 1);
	    if(-1 == $reg_left_operand) {
		return -1;
	    }

	    $ret_code = handle_copy("00temp", $left_operand);
	    if(-1 == $ret_code) {
		return -1;
	    }

	    printf(DEST_FILE "$comparator R$reg_right_operand,R$reg_left_operand,:$label\n");

	    $ret_code = free_register("00temp");
	    if(-1 == $ret_code) {
		return -1;
	    }
	    return 0;
	}
    }
    
    if( $right_operand =~ m/^-?\d+$/ ) {
	if( $left_operand =~ m/^-?\d+$/ ) {
	    printf(STDERR "syntax error ");
	    return -1;
	} else {
	    $reg_left_operand = get_register($left_operand, 0);
	    if(-1 == $reg_left_operand) {
		return -1;
	    }

	    $reg_right_operand = get_register("00temp", 1);
	    if(-1 == $reg_right_operand) {
		return -1;
	    }

	    $ret_code = handle_copy("00temp", $right_operand);
	    if(-1 == $ret_code) {
		return -1;
	    }

	    printf(DEST_FILE "$comparator R$reg_right_operand,R$reg_left_operand,:$label\n");

	    $ret_code = free_register("00temp");
	    if(-1 == $ret_code) {
		return -1;
	    }
	    return 0;
	}
    }
    
    $reg_left_operand = get_register($left_operand, 0);
    if(-1 == $reg_left_operand) {
	return -1;
    }

    $reg_right_operand = get_register($right_operand, 0);
    if(-1 == $reg_right_operand) {
	return -1;
    }

    printf(DEST_FILE "$comparator R$reg_right_operand,R$reg_left_operand,:$label\n");
    
    return 0;
}

sub handle_ineq
{
    my ($left_operand, $right_operand, $label, $comparator) = @_;
    my $ret_code = 0;
    my $register = 0;
    my $reg_left_operand = 0;
    my $reg_right_operand = 0;

    $register = get_register("00temp", 1);
    if(-1 == $register) {
	return -1;
    }

    $ret_code = handle_rimm("00temp", $left_operand, $right_operand, "sub");
    if(-1 == $ret_code) {
	return -1;
    }

    printf(DEST_FILE "$comparator R$register,:$label\n");
    
    $ret_code = free_register("00temp");
    if(-1 == $ret_code) {
	return -1;
    }

    return 0;
}

sub handle_comparator
{
    my ($operator,  $left_operand, $right_operand, $label) = @_;
    my $ret_code = 0;

    if( $operator eq "==" ) {
	$ret_code = handle_eq($left_operand, $right_operand, $label, "bne");	
    } elsif( $operator eq "!=" ) {
	$ret_code = handle_eq($left_operand, $right_operand, $label, "beq");
    } elsif( $operator eq "<=" ) {
	$ret_code = handle_ineq($left_operand, $right_operand, $label, "bgtz");
    } elsif( $operator eq ">=" ) {
	$ret_code = handle_ineq($left_operand, $right_operand, $label, "bltz");
    } elsif( $operator eq ">" ) {
	$ret_code = handle_ineq($left_operand, $right_operand, $label, "blez");
    } elsif( $operator eq "<" ) {
	$ret_code = handle_ineq($left_operand, $right_operand, $label, "bgez");
    } else {
	printf(STDERR "unknown comparator '$operator' ");
	return -1;
    }
       
    return $ret_code;
}

sub handle_if
{
    my ($operator, $left_operand, $right_operand) = @_;

    my $label = 0;
    my $ret_code = 0;

    $label = "end_then_$num_if";
    push(@context, $label);
    $num_if = $num_if + 1;

    if( 1 == $verbose ) {
	    printf("$label pushed\n");
    }

    $ret_code = handle_comparator($operator, $left_operand, $right_operand, $label);
  
    return $ret_code;
}

sub handle_else
{
    my $label = 0;
    my $label_end_else = 0;
    my $cross = 0;

    if( scalar(@context) == 0 ) {
	printf(STDERR "'else' without 'if' ");
	return -1;
    }
    $label = pop(@context);
    
    if( 1 == $verbose ) {
	printf("$label poped\n");
    }

    if( $label !~ m/end_then_(\d)+/ ) {
	$label =~ m/end_(\w+)_\d+/;
	if( "while" eq $1 ) {
	    $cross = "while";
	} elsif( "else" eq $1 ) {
	    $cross = "if";
	} else {
	    $cross = "for";
	}
	printf(STDERR "context crossed ('else' before 'end$cross') ");
	return -1;
    }

    $label_end_else = "end_else_$1";
    printf(DEST_FILE "j :$label_end_else\n");

    push(@context, $label_end_else);
    handle_label($label);

    if( 1 == $verbose ) {
	printf("$label_end_else pushed\n");
    }

    return 0;
}

sub handle_endif
{
    my $label = 0;
    my $cross = 0;

    if( scalar(@context) == 0 ) {
	printf(STDERR "'endif' without 'if' ");
	return -1;
    }
    $label = pop(@context);
    
    if( 1 == $verbose ) {
	printf("$label poped\n");
    }

    if( $label !~ m/end_then_\d+/ && $label !~ m/end_else_\d+/) {
	$label =~ m/end_(\w+)_\d+/;
	if( "while" eq $1 ) {
	    $cross = "while";
	} else {
	    $cross = "for";
	}
	printf(STDERR "context crossed ('endif' before 'end$cross') ");
	return -1;
    }

    handle_label($label);

    return 0;
}

sub handle_while
{    
    my ($operator, $left_operand, $right_operand) = @_;
    my $label = 0;
    my $label_end = 0;
    my $ret_code = 0;

    $label_end = "end_while_$num_while";
    $label = "while_$num_while";
    push(@context, $label_end);
    $num_while = $num_while + 1;

    if( 1 == $verbose ) {
	printf("$label_end pushed\n");
    }

    handle_label($label);
    $ret_code = handle_comparator($operator, $left_operand, $right_operand, $label_end);
  
    return $ret_code;
}

sub handle_endwhile
{
    my $label_end = 0;
    my $label = 0;
    my $cross = 0;

    if( scalar(@context) == 0 ) {
	printf(STDERR "'endwhile' without 'while' ");
	return -1;
    }
    $label_end = pop(@context);

    if( 1 == $verbose ) {
	    printf("$label_end poped\n");
    }

    if( $label_end !~ m/end_(while_\d+)/ ) {
	$label_end =~ m/end_(\w+)_\d+/;
	if( "then" eq $1 || "else" eq $1 ) {
	    $cross = "if";
	} else {
	    $cross = "for";
	}
	printf(STDERR "context crossed ('endwhile' before 'end$cross') ");
	return -1;
    }

    $label = $1;
    printf(DEST_FILE "j :$label\n");
    handle_label($label_end);

    return 0;
}

sub handle_for
{
    my ($variable, $from, $to, $step) = @_;
    my $label = 0;
    my $label_end = 0;
    my $ret_code = 0;

    $label_end = "end_for_$num_for";
    $label = "for_$num_for";
    push(@context, $label_end);
    $num_for = $num_for + 1;

    if( 1 == $verbose ) {
	printf("$label_end pushed\n");
    }

    $ret_code = handle_copy($variable, $from);
    if(-1 == $ret_code) {
	return -1;
    }

    handle_label($label);

    $ret_code = handle_comparator("<=", $variable, $to, $label_end);
    if(-1 == $ret_code) {
	return -1;
    }

    $ret_code = handle_rimm($variable, $variable, $step, "add");
    if(-1 == $ret_code) {
	return -1;
    }

    return $ret_code;
}

sub handle_endfor
{
    my $label_end = 0;
    my $label = 0;
    my $cross = 0;

    if( scalar(@context) == 0 ) {
	printf(STDERR "'endfor' without 'for' ");
	return -1;
    }
    $label_end = pop(@context);

    if( 1 == $verbose ) {
	    printf("$label_end poped\n");
    }

    if( $label_end !~ m/end_(for_\d+)/ ) {
	$label_end =~ m/end_(\w+)_\d+/;
	if( "then" eq $1 || "else" eq $1 ) {
	    $cross = "if";
	} else {
	    $cross = "for";
	}
	printf(STDERR "context crossed ('endfor' before 'end$cross') ");
	return -1;
    }

    $label = $1;
    printf(DEST_FILE "j :$label\n");
    handle_label($label_end);

    return 0;
}

sub handle_goto
{
    my ($label) = @_;
    my $register = 0;
    
    if( $label =~ m/\$(\w+)/ ) {
	$label = $1;
	if( defined( $registres{"$label"} )) {
	    $register = $registres{"$label"};
	    printf(DEST_FILE "jr R$register\n");
	} else {
	    return -1;
	}
    } else {
	printf(DEST_FILE "j :$label\n");
    }

    return 0;
}

sub handle_label
{
    my ($label) = @_;
    printf(DEST_FILE ":$label\n");
    return 0;
}

sub handle_break
{
    my $label = 0;
    my $i = 0;

    for( $i = @context - 1; $i >= 0 ; $i--) {
	$label = $context[$i];
	if( $label =~ m/end_while_\d+/ || $label =~ m/end_for_\d+/ ) {
	    printf(DEST_FILE "j :$label\n");
	    return 0;
	}
    }

    printf(STDERR "'break' not inside 'while' or 'for' context ");
    return -1;    
}

sub handle_continue
{
    my $label = 0;
    my $i = 0;

    for( $i = @context - 1; $i >= 0 ; $i--) {
	$label = $context[$i];
	if( $label =~ m/end_(while_\d+)/ || $label =~ m/end_(for_\d+)/ ) {
	    printf(DEST_FILE "j :$1\n");
	    return 0;
	}
    }

    printf(STDERR "'continue' not inside 'while' or 'for' context ");
    return -1;
}

sub parse_line
{
    my ($line) = @_;
    my $result = 0;
    my $left_operand = 0;
    my $operator = 0;
    my $right_operand = 0;
    my $label = 0;
    my $ret_code = -1;

    if( 1 == $debug and $line !~ m/^\s*\/\//i ) {
	printf(DEST_FILE "//$line");
    }

    if( $line =~ m/^\s*(\w+)\s*=\s*(-?\w+)\s*([-+&\|<>]+|\s+xor\s+|\s+nor\s+)\s*(-?\w+)\s*$/ ) {
    	$result = $1;
	$left_operand = $2;
	$operator = $3;
	$right_operand = $4;
	$ret_code = handle_operation($operator, $result, $left_operand, $right_operand); 
    } elsif( $line =~ m/^\s*(\w+)\s*=\s*mem\s*\[\s*(\w+)\s*,\s*(\w+)\s*\]\s*$/i ) {
	$result = $1;
	$left_operand = $2;
	$right_operand = $3;
	$ret_code = handle_mem($result, $left_operand, $right_operand, "lw");
    } elsif( $line =~ m/^\s*mem\s*\[\s*(\w+)\s*,\s*(\w+)\s*\]\s*=\s*(\w+)\s*$/i ) {
	$result = $3;
	$left_operand = $1;
	$right_operand = $2;
	$ret_code = handle_mem($result, $left_operand, $right_operand, "sw");
    } elsif( $line =~ m/^\s*if\s+(-?\w+)\s*([<>=!]+)\s*(-?\w+)\s*then\s*$/i ) {
	$left_operand = $1;
	$operator = $2;
	$right_operand = $3;	
	$ret_code = handle_if($operator, $left_operand, $right_operand);
    } elsif( $line =~ m/^\s*else\s*$/i ) {	
	$ret_code = handle_else();
    } elsif( $line =~ m/^\s*endif\s*$/i ) {
	$ret_code = handle_endif();
    } elsif( $line =~ m/^\s*while\s+(-?\w+)\s*([<>=!]+)\s*(-?\w+)\s*do\s*$/i ) {
	$left_operand = $1;
	$operator = $2;
	$right_operand = $3;
	$ret_code = handle_while($operator, $left_operand, $right_operand);
    } elsif( $line =~ m/^\s*endwhile\s*$/i ) {
	$ret_code = handle_endwhile();
    } elsif( $line =~ m/^\s*for\s+(\w+)\s+from\s+(-?\d+)\s+to\s+(-?\d+)\s+step\s+(-?\d+)\s+do\s*$/i ) {
	$result = $1;
	$left_operand = $2;
	$right_operand = $3;
	$operator = $4;
	$ret_code = handle_for($result, $left_operand, $right_operand, $operator);
    } elsif( $line =~ m/^\s*endfor\s*$/i ) {
	$ret_code = handle_endfor();
    } elsif( $line =~ m/^\s*\/\//i ) {
	#handle_comment();
	if( 0 == $strip ) {
	    printf(DEST_FILE "$line");
	}
	$ret_code = 0;
    } elsif( $line =~ m/^\s*$/i ) {
	#handle_empty_line();
	if( 0 == $strip ) {
	    printf(DEST_FILE "\n");
	}
	$ret_code = 0;
    } elsif( $line =~ m/^\s*(\w+)\s*(=)\s*(-?\w+)\s*$/i ) {
	$result = $1;
	$operator = $2;
	$right_operand = $3;
	$left_operand = $3;
	$ret_code = handle_operation($operator, $result, $left_operand, $right_operand);
    } elsif( $line =~ m/^\s*goto\s+(\$?\w+)\s*$/i ) {
	$label = $1;
	$ret_code = handle_goto($label);
    } elsif( $line =~ m/^\s*label\s+(\w+)\s*$/i ) {
	$label = $1;
	$ret_code = handle_label($label);
    } elsif( $line =~ m/^\s*free\s+(\w+)\s*$/i) {
	$result = $1;
	printf(STDERR "DON'T USE FREE, PLEASE !\n");
	$ret_code = free_register($result);
    } elsif( $line =~ m/^\s*continue\s*$/i) {
	$result = $1;
	$ret_code = handle_continue();
    } elsif( $line =~ m/^\s*break\s*$/i) {
	$result = $1;
	$ret_code = handle_break();
    } else {
	printf(STDERR "syntax error ");
    }

    return $ret_code;
}

sub parse_file
{
    my $check_line = 0;
    my $num_line = 0;
 
    while( <SOURCE_FILE> ) {
	$num_line = $num_line + 1;
	$check_line = parse_line($_);
	if(-1 == $check_line) {
	    printf(STDERR "at line $num_line :\n>> $_");
	    return -1;
	}
    }

    return 0;
}

sub usage
{
    print("usage: $0 [OPTIONS]\n");
    print("options are:\n");
    print("\t -s SOURCE , source code file\n");
    print("\t -d DESTINATION , destination asm file\n");
    print("\t -v , verbose screen output\n");
    print("\t -g , debug asm output\n");
    print("\t -t , stripped asm output\n");
    print("\t -h , print this help and store a French tutorial in tutorial.code\n\n");
}

sub tuto
{
    open(TUTO_FILE, ">tutorial.code") or die("open(tutorial.code) : $!");
    printf(TUTO_FILE "//                                   R3KCompiler
// 
//    Resume
// 
//    Ce document est un tutoriel pour le compilateur R3KCompiler. Cet outils
//    permet de generer, a partir d'un code en langage evolue, du code
//    assembleur compatible avec les outils du projet <<Processeur MIPS
//    R3000>> du Master CAMSI.
// 
//    Telechargez R3KCompiler ici : R3KCompiler
// 
//    Copyright (c) 2004, 2005
// 
//      ----------------------------------------------------------------------
// 
//    Table des matieres
// 
//    1. Variables et syntaxe
// 
//    2. Operations
// 
//    3. Memoire
// 
//    4. Iterations
// 
//    5. Comparaisons et conditions
// 
//    6. Gestion des registres
// 
//    7. Sauts
// 
//    8. Invocation
// 
// 1.Variables et syntaxe
// 
//    Comme en C, toutes les variables doivent etre declarees avant leur
//    utilisation. Cependant la syntaxe de ce langage est telle que toutes les
//    variables sont du type entier signe 32 bits. Il est donc inutile de
//    preciser un type... J'entends deja les mauvaises langues dire <<mais
//    qu'est que c'est ce langage tout pourri...>>, je leur repondrais :
// 
//      o ce projet est libre, donc s'il ne vous convient pas, modifiez le !
//      o ce projet est avant tout un <<proof of concept>>, pour montrer
//        que notre processeur <<fait maison>> peut executer des
//        algorithmes sympathiques, et pas seulement des additions ou autres.
//      o en 3 jours, je ne pouvais pas faire mieux, surtout que je n'avais
//        presque jamais programme en Perl...
// 
//    Revenons-en a nos moutons, voila comment declarer des variables, avec
//    une valeur par defaut :
//
  iterator = 1
  temp1 = 2
  temp2 = 0
  temp3 = 5
//
//    Vous avez du remarquer qu'il n'y avait pas de ; en fin de ligne. En fait
//    la syntaxe de ce langage n'autorise qu'une seule operation par ligne^[1].
//    Pour faire une addition par exemple, on peut ecrire :
//
  temp2 = temp1 + 2
//
//    Mais on ne peut pas faire : iterator = temp + 4 - 2
//
//    Pour mettre en commentaire une ligne, il suffit de la faire commencer par
//    //.
//
// 2.Operations
//
//    En ce qui concerne les operations que l'on peut faire, nous avons le choix
//    entre :
//
//      o l'addition :
  temp1 = temp3 + temp2
//      o la soustraction :
  temp3 = 3 - temp1
//      o le et logique :
  temp2 = temp2 & temp2
//      o le ou logique :
  temp3 = temp1 | 0
//      o le decalage a gauche (l'operande de droite doit etre une variable) :
  temp1 = temp1 << 1
//      o le decalage a droite (l'operande de gauche doit etre une variable) :
  temp1 = 2 >> temp3
//      o le positionner si inferieur (ou egale, tout depend du context) :
  temp1 = 0 <| temp2
//      o l'affectation :
  temp3 = temp1
//      o le non ou logique (toutes les operandes doivent etre des variables) :
  temp2 = temp1 nor temp2
//      o le ou exclusif logique :
  temp1 = temp2 xor temp3
//
//    Comme vous l'avez lu, il existe certaines contraintes, lies a notre
//    processeur, en ce qui concerne l'utilisation des operations. Lorsque j'ai
//    eu le temps, j'ai fais en sorte de passer outre ces limitations, mais il
//    en reste quelques unes...
//
// 3.Memoire
//
//    Commencons a jouer avec la memoire. Pour une ecriture en memoire, il
//    suffit de faire :
//
  mem[10,temp2] = temp1
//
//    Il faut bien respecter le type des operandes, cette limitation est encore
//    due a notre architecture, et je n'ai pas eu le temps de contourner le
//    probleme. En ce qui concerne la lecture en memoire, c'est le meme principe
//    :
//
  temp3 = mem[21,temp1]
//
// 4.Iterations
//
//    Nous avons vu les variables, les operations, et la memoire, continuons
//    avec les boucles ! En ce qui concerne les boucles for, il faut preciser la
//    variable d'iteration, le debut, la fin et le pas :
//
  for iterator from 0 to 100 step 2 do
          // something to do
  endfor
//
//    Une autre maniere de boucler consiste a utiliser le while :
//
  while temp1 >= temp2 do
          // something to do
  endwhile
//
//    J'allais oublier quelque chose a propos des boucles ;) ! On peut utiliser
//    les mot clefs break pour terminer la boucle (for ou while) en plein
//    milieu, ou continue pour sauter une iteration, comme en C :
//
  for iterator from 0 to 100 step 2 do
          // something to check
          break
  endfor
//
  while temp1 >= temp2 do
          // something to check
          continue
  endwhile
//
// 5.Comparaisons et conditions
//
//    Toutes les conditions de comparaisons sont possibles :
//
//      o egalite : ==
//      o difference : !=
//      o inferieur : < et l'equivalent large : <=
//      o superieur : > et l'equivalent large : >=
//
//    Tout cela nous amene au branchement if :
//
  if temp1 != temp3 then
          // something to do
  endif
//
//    Nous pouvons aussi utiliser le else :
//
  if temp1 < temp3 then
          // something to do
  else
          // something else to do
  endif
//
// 6.Gestion des registres
//
//    Nous voila deja a l'avant dernier chapitre... Si vous avez regarde le
//    code source de R3KCompiler, vous avez du remarquer l'allocateur de
//    registre (c'est la premiere fonction). C'est en fait un dedicateur de
//    registres dans le sens ou lorsqu'une variable est affectee a un registre,
//    c'est de maniere definitive... Le seul moyen de liberer un registre est
//    d'utiliser le mot clef free :
//
  free iterator
//
//    Mais il faut etre sur de ce que l'on fait !!! Je n'ai mis cette
//    fonctionnalite juste pour me simplifier la vie, elle devrait etre
//    interdite ! En effet, imaginez qu'on libere une variable, avec les sauts,
//    on ne peut pas predire facilement si c'est le meme registre qui sera
//    utilise^[2]...
//
// 7.Sauts
//
//    Enfin, comme tout mauvais langage de programmation, il gere aussi les goto
//    et les label...
//
  label pas_beau
          // something to do
  goto pas_beau
//
//    J'avais commence a reflechir sur les fonctions, donc il est possible de
//    sauter a une adresse stoquee dans un registre (dedie a la variable temp1
//    dans cet exemple) :
//
  goto \$temp1
//
// 8.Invocation
//
//    Voila, nous avons fait le tour d'horizon de ce fabuleux langage nomme...
//    euh... En fait c'est un langage sans nom :) !
//
//    En ce qui concerne le compilateur, voila une liste des options qu'il gere
//    :
//
//      o -s specifie le nom du fichier source
//      o -d specifie le nom du fichier de destination
//      o -v affiche l'allocation de registre et les contextes
//      o -g enregistre dans le fichier source le code source en commentaire
//      o -t n'enregistre pas dans le fichier source les commentaires d'origine
//      o -h devinez ;) !
//
//    Amusez-vous a compiler l'aide tutoriel en faisant :
//
//  ./r3kcompiler -h
//  ./r3kcompiler -s tutorial.code -d tutorial.asm -g -t
//
//    Vous trouverez dans tutorial.asm le code assembleur de ce tutoriel :)
//
//    Il ne reste plus qu'a l'assembler avec votre assembleur favoris (mais ce code risque de ne pas faire grand chose d'utile !).
//
//    Merci de votre lecture (je crois n'avoir rien oublie) !
//
//    --------------
//
//    ^[1] pour ceux qui ne sont pas contents, c.f. paragraphe precedent
//
//    ^[2] Voir mon rapport sur les allocateurs de registres.\n");
}

sub main
{
   my %opts;
   my $check_file = -1;
   my $label = 0;
   my $i = 0;

   print("r3kcompiler, Copyright (C) 2005 Cedric VINCENT <cedric.vincent\@gmail.com>\n");
   print("r3kcompiler (release 060718) comes with ABSOLUTELY NO WARRANTY\n");
   print("This is free software, and you are welcome to copy, study, modify, and redistribute it under GPL\n\n");

   if( !getopts("s:d:vgth", \%opts) ) {
       usage();
       exit(1);
   }

   if( exists($opts{h}) ) {
       usage();
       tuto();
       exit(1);
   }

   if( !exists($opts{s}) || !exists($opts{d}) ) {
       usage();
       exit(1);
   }

   if( exists($opts{v}) ) {
       $verbose = 1;
   } else {
       $verbose = 0;
   }

   if( exists($opts{g}) ) {
       $debug = 1;
   } else {
       $debug = 0;
   }

   if( exists($opts{t}) ) {
       $strip = 1;
   } else {
       $strip = 0;
   }

   open(SOURCE_FILE, "<$opts{s}") or die("open($opts{s}) : $!");
   open(DEST_FILE, ">$opts{d}") or die("open($opts{d}) : $!");

   $num_allocated_register = 1;
   $num_while = 0;
   $num_if = 0;
   $num_for = 0;
   for( $i = 0; $i <= 31; $i++) {
       $free_reg[$i] = 0;
   }

   $check_file = parse_file();
   if( -1 == $check_file ) {
       printf(STDERR "failed to compile $opts{s}\n");
       return -1;
   }

   if( scalar(@context) != 0 ) {
       $label = pop(@context);
       $label =~ m/end_(\w+)_\d+/;
       if( "while" eq $1 ) {
	   $label = "while";
       } elsif( "else" eq $1 || "then" eq $1 ) {
	   $label = "if";
       } else {
	   $label = "for";
       }
       printf(STDERR "unbalanced context at end of file ('end$label' missing) \n");
       printf(STDERR "failed to compile $opts{s}\n");
       return -1;
   }

   print("Compilation succeeded !\n");

}

