unit evalue;
(* Evaluateur d'expression de PetitBasic. *)

interface

uses numeriques, chaines, logiques, variables;

function 	evalue_expression_numerique (p : EXPRESSION_NUMERIQUE;
			var reel : boolean) : real;
function 	evalue_expression_chaine (c : EXPRESSION_CHAINE) : string;
function 	evalue_expression_logique (l : EXPRESSION_LOGIQUE)
			: boolean;

implementation

uses interpreteur;

function evalue_expression_numerique (p : EXPRESSION_NUMERIQUE;
	var reel : boolean) : real;
(* Evalue une expression num‚rique dont l'arbre d'expression est p avec l'
 * aide de la variable globale reel.  La variable globale reel doit etre
 * initialis‚ … true si l'on veut un r‚sultat r‚el ou false si l'on veut un
 * r‚sultat entier.  Dans tous les cas si l'une des op‚randes est un r‚el,
 * alors le r‚sultat sera un r‚el, il y a conversion automatique pour un
 * maximum de pr‚cision.  La valeur renvoy‚e est toujours un r‚el mais en
 * fait il s'agit d'un nombre entier si reel est ‚gal … false.  Le r‚sultat
 * n'est valide que si erreur_interpreteur est ‚gale … INTERPRETEUR_OKAY.
 *)

function divise (r, s : real) : real;
(* Divise r par s et renvoie le r‚sultat.  Si s est nul alors il y a une
 * erreur (division par z‚ro), on signale l'erreur et on renvoie z‚ro.
 *)

begin
	if s = 0.0 then begin
		erreur_interpreteur := DIVISION_PAR_ZERO;
		divise := 0.0;
	end else begin
		if reel then
			divise := r / s
		else
			divise := round(r) div round(s);
	end;
end;

function reste (r, s : real) : real;
(* Calcule le modulo de r par s et renvoie le r‚sultat.  Si s est nul alors
 * il y a une erreur (division par z‚ro), on signale l'erreur et on renvoie
 * z‚ro.
 *)

begin
	if s = 0.0 then begin
		erreur_interpreteur := DIVISION_PAR_ZERO;
		reste := 0.0;
	end else begin
		if reel then
			reste := r - s * int(r / s)
		else
			reste := round(r) mod round(s);
	end;
end;

var	v	: VARIABLE;
	r, s, t	: real;

begin
	if erreur_interpreteur <> INTERPRETEUR_OKAY then

		evalue_expression_numerique := 0.0

	else if p = nil then begin

		writeln('Erreur Interne: ProblŠme avec l''‚valuateur ',
			'd''expressions num‚riques!');
		halt;

	end else if p^.type_expression = NUMERIQUE_ENTIERE then begin

		evalue_expression_numerique := p^.entier

	end else if p^.type_expression = NUMERIQUE_REELLE then begin

		reel := true;
		evalue_expression_numerique := p^.reel;

	end else if p^.type_expression = NUMERIQUE_VARIABLE then begin

		v := cherche_variable(p^.identificateur);
		if v = nil then begin
			if p^.type_numerique = VALEUR_ENTIERE then begin
				v := cree_variable_entiere(
					p^.identificateur, 0);
				evalue_expression_numerique := 0
			end else begin
				v := cree_variable_reelle(
					p^.identificateur, 0.0);
				reel := true;
				evalue_expression_numerique := 0.0
			end;
		end else begin
			if v^.type_variable = VARIABLE_ENTIERE then
				evalue_expression_numerique := v^.entier
			else begin
				reel := true;
				evalue_expression_numerique := v^.reel;
			end;
		end;

	end else if p^.type_expression = NUMERIQUE_BINAIRE then begin

		r := evalue_expression_numerique(p^.gauche, reel);
		s := evalue_expression_numerique(p^.droite, reel);
		if erreur_interpreteur <> INTERPRETEUR_OKAY then
			evalue_expression_numerique := 0.0
		else begin
			case p^.binaire of
				ADDITION  	: t := r + s;
				SOUSTRACTION  	: t := r - s;
				MULTIPLICATION	: t := r * s;
				DIVISION	: t := divise(r, s);
				MODULO		: t := reste(r, s);
			end;
			evalue_expression_numerique := t;
		end;

	end else begin	(* expression unaire *)

		t := evalue_expression_numerique(p^.unique, reel);
		if erreur_interpreteur <> INTERPRETEUR_OKAY then
			evalue_expression_numerique := 0.0
		else begin
			if p^.unaire = MOINS then
				evalue_expression_numerique := -t
			else
				evalue_expression_numerique := t;
		end;

	end;
end;

function evalue_expression_chaine (c : EXPRESSION_CHAINE) : string;
(* Evalue une expression chaine dont l'arbre d'expression est c.  Le r‚sultat
 * n'est … considerer valide que si la variable erreur_interpreteur est ‚gale
 * … INTERPRETEUR_OKAY.
 *)

const 	LONGUEUR_MAXIMUM	= 255;

var	v 	: VARIABLE;
	s, t	: string;

begin
	if erreur_interpreteur <> INTERPRETEUR_OKAY then

		evalue_expression_chaine := ''

	else if c = nil then begin

		writeln('Erreur Interne: ProblŠme avec l''‚valuateur ',
			'd''expressions chaines!');
		halt;

	end else if c^.type_expression = CHAINE_LITERAL then begin

		evalue_expression_chaine := c^.chaine;

	end else if c^.type_expression = CHAINE_VARIABLE then begin

		v := cherche_variable(c^.identificateur);
		if v = nil then begin
			v := cree_variable_chaine(c^.identificateur, '');
			evalue_expression_chaine := '';
		end else
			evalue_expression_chaine := v^.chaine;

	end else begin	(* expression concat‚nation *)

		s := evalue_expression_chaine(c^.gauche);
		t := evalue_expression_chaine(c^.droite);
		if erreur_interpreteur <> INTERPRETEUR_OKAY then
			evalue_expression_chaine := ''
		else begin
			if length(s) + length(t) > LONGUEUR_MAXIMUM then
			begin
				erreur_interpreteur
					:= CONCATENATION_TROP_LONGUE;
				evalue_expression_chaine := '';
			end else
				evalue_expression_chaine := s + t;
		end;
	end;
end;

function evalue_expression_logique (l : EXPRESSION_LOGIQUE) : boolean;
(* Evalue une expression logique dont l'arbre d'expression est l, et renvoie
 * un booleen.  Ce booleen est valide si la variable erreur_interpreteur est
 * ‚gale … INTERPRETEUR_OKAY.
 *)

var     reel		: boolean;
	a, b		: real;
	s, t		: string;
	temp		: boolean;
	gauche, droite  : boolean;

begin
	if erreur_interpreteur <> INTERPRETEUR_OKAY then

		evalue_expression_logique := false

	else if l = nil then begin

		writeln('Erreur Interne: ProblŠme avec l''‚valuateur ',
			'd''expressions logiques!');
		halt;

	end else if l^.type_expression = LOGIQUE_RELATIONNELLE then begin

		if l^.type_relation = RELATION_NUMERIQUE then begin
			reel := false;
			a := evalue_expression_numerique(l^.a, reel);
			b := evalue_expression_numerique(l^.b, reel);
			if erreur_interpreteur <> INTERPRETEUR_OKAY then
				evalue_expression_logique := false
			else begin
				case l^.operateur of
					PLUS_PETIT 	: temp := a < b;
					PLUS_PETIT_EGAL	: temp := a <= b;
					PLUS_GRAND      : temp := a > b;
					PLUS_GRAND_EGAL : temp := a >= b;
					EGAL      	: temp := a = b;
					DIFFERENT	: temp := a <> b;
				end;
				evalue_expression_logique := temp;
			end;
		end else begin
			s := evalue_expression_chaine(l^.c);
			t := evalue_expression_chaine(l^.d);
			if erreur_interpreteur <> INTERPRETEUR_OKAY then
				evalue_expression_logique := false
			else begin
				case l^.operateur of
					PLUS_PETIT 	: temp := s < t;
					PLUS_PETIT_EGAL	: temp := s <= t;
					PLUS_GRAND      : temp := s > t;
					PLUS_GRAND_EGAL : temp := s >= t;
					EGAL      	: temp := s = t;
					DIFFERENT	: temp := s <> t;
				end;
				evalue_expression_logique := temp;
			end;
		end;

	end else if l^.type_expression = LOGIQUE_BINAIRE then begin

		gauche := evalue_expression_logique(l^.gauche);
		droite := evalue_expression_logique(l^.droite);
		if erreur_interpreteur <> INTERPRETEUR_OKAY then
			evalue_expression_logique := false
		else begin
			case l^.binaire of
				ET 		: temp := gauche and droite;
				OU 		: temp := gauche or droite;
				OU_EXCLUSIF 	: temp := gauche xor droite;
			end;
			evalue_expression_logique := temp;
		end;

	end else begin	(* expression unaire *)

		temp := not evalue_expression_logique(l^.unique);
		if erreur_interpreteur <> INTERPRETEUR_OKAY then
			evalue_expression_logique := false
		else
			evalue_expression_logique := temp;

	end;
end;

end.