Précédent Index Suivant

5.3   Description de l'évaluateur

Un évaluateur ML prend en entrée un environnement E, une expression e et retourne une valeur v. Un environnement est implanté comme une liste d'association "nom" - "valeur". Les expressions sont représentées par le type ml_expr suivant : type ml_expr = Const of ml_const
* 2 Var of string
* 2 Unop of string * ml_expr
* 2 Binop of string * ml_expr * ml_expr
* 2 Pair of ml_expr * ml_expr
* 2 Cons of ml_expr * ml_expr
* 2 Cond of ml_expr * ml_expr * ml_expr
* 2 App of ml_expr * ml_expr
* 2 Abs of string * ml_expr
* 2 Letin of string * ml_expr * ml_expr
* 2 Letrecin of string * ml_expr * ml_expr
* 2 Ref of ml_expr
* and ml_const = Int of int
* 2 Float of float
* 2 Bool of bool
* 2 String of string
* 2 Emptylist
* 2 Unit
* ;;
* Type ml_expr defined.
* Type ml_const defined. On remarque par rapport au typeur, l'introduction de primitives unaire et binaire dans le type ml_expr. Elles correspondent aux primitives arithmétiques (+,- ...), de comparaisons (=,< ...), d'accès aux paires et aux listes.

Le type ml_val, correspondant aux valeurs calculées, est le suivant : type ml_val =
* V_const of v_mlconst
* V_list of ml_val * ml_val
* V_pair of ml_val * ml_val
* V_closure of (string * ml_val) list * ml_expr
* V_ref of ml_val ref
* and v_mlconst =
* V_int of int
* V_float of float
* V_bool of bool
* V_string of string
* V_emptylist
* V_unit;;
* Type ml_val defined.
* Type v_mlconst defined.

L'évaluation d'une expression peut entrainer une erreur de calcul dans certains cas. Le premier est la construction récursive d'une valeur non fonctionnelle. Cette erreur pourrait être détectée par le typeur, mais ce n'est pas le cas. Le deuxième cas provient d'une erreur de calcul d'une primitive qui n'est pas définie sur l'ensemble des valeurs du type de son argument. Par exemple une division par zéro a quelques difficultés pour retourner une valeur, de même calculer la tête d'une liste vide. Pour cela on définit les deux exceptions suivantes : exception Rec_def;;
* Exception Rec_def defined. exception Eval_failure of string;;
* Exception Eval_failure defined. qui seront traitées par l'évaluateur.

Voici les principales fonctions d'évaluation. L'évaluation d'une constante est réalisée par la fonction suivante : let eval_ml_const c = match c with
* Int i ® V_int i
* Float f ® V_float f
* Bool b ® V_bool b
* String s ® V_string s
* Emptylist ® V_emptylist
* Unit ® V_unit
* ;;
* eval_ml_const : ml_const ® v_mlconst = áfunñ

Les primitives uniares et binaires sont prédéfinies en dur dans les fonctions eval_unop et eval_binop suivantes : let rec eval_unop u v = match (u,v) with
* (``!'', V_ref v) ® !v
* (``_ML_hd'', V_list(a,)) ® a
* (``_ML_hd'', V_const V_emptylist) ® raise (Eval_failure ``hd'')
* (``_ML_tl'', V_list(,b)) ® b
* (``_ML_tl'', V_const V_emptylist) ® raise (Eval_failure ``tl'')
* (``_ML_fst'',V_pair(a,)) ® a
* (``_ML_snd'',V_pair(,b)) ® b
* (s,) ® failwith (``primitive unaire inconnue : ''^s)
* ;;
* eval_unop : string ® ml_val ® ml_val = áfunñ

let eval_binop f a b = match (f,a,b) with
* (``+'',V_const(V_int a),V_const(V_int b)) ® V_const(V_int(a+b))
* (``-'',V_const(V_int a),V_const(V_int b)) ® V_const(V_int(a-b))
* (``*'',V_const(V_int a),V_const(V_int b)) ® V_const(V_int(a*b))
* (``/'',V_const(V_int a),V_const(V_int b)) ®
* begin
* try V_const(V_int(a/b)) with Division_by_zero ® raise (Eval_failure ``Div0'')
* end
* (``='',a,b) ® V_const(V_bool(a = b))
* (``+.'',V_const(V_float a),V_const(V_float b)) ® V_const(V_float(a+.b))
* (``-.'',V_const(V_float a),V_const(V_float b)) ® V_const(V_float(a-.b))
* (``*.'',V_const(V_float a),V_const(V_float b)) ® V_const(V_float(a*.b))
* (``/.'',V_const(V_float a),V_const(V_float b)) ® V_const(V_float(a/.b))
* (``:='',V_ref v1, v2) ® v1:=v2V_const(V_unit)
* (s,,) ® failwith (``primitive binaire inconnue : ''^s)
* ;;
* eval_binop : string ® ml_val ® ml_val ® ml_val = áfunñ Ces fonctions travaillent sur des données de type ml_val.

Enfin la fonction principale d'évaluation traite les différentes constructions du langage selon les règles vues à la section précédente (qui sont lues de bas en haut pour écrire le traitement à effectuer).

let rec eval_ml_expr env expr = match expr with
* Const c ® V_const (eval_ml_const c)
* Var v ® assoc v env
* Pair (e1,e2) ® V_pair(eval_ml_expr env e1,eval_ml_expr env e2)
* Cons (e1,e2) ® V_list(eval_ml_expr env e1,eval_ml_expr env e2)
* Cond (e1,e2,e3) ®
* begin
* match eval_ml_expr env e1 with
* V_const (V_bool b) ®
* if b then eval_ml_expr env e2 else eval_ml_expr env e3
* ® failwith ``eval_ml_expr : erreur interne : pas de bool''
* end
* Unop (s,e) ® let v = eval_ml_expr env e in eval_unop s v
* Binop(s,e1,e2) ® let v1 = eval_ml_expr env e1
* and v2 = eval_ml_expr env e2 in
* eval_binop s v1 v2
* App(e1,e2) ®
* begin
* match (eval_ml_expr env e1, eval_ml_expr env e2) with
* (V_closure(clos_env,Abs(x,body)),v) ®
* eval_ml_expr ((x,v)::clos_env) body
* ® failwith ``eval_ml_expr : erreur interne : pas de fun''
* end
* Abs(p,e) ® V_closure(env,expr)
* Letin(s,e1,e2) ®
* let v = eval_ml_expr env e1 in
* eval_ml_expr ((s,v)::env) e2
* Letrecin(f,e1,e2) ®
* begin
* match e1 with
* Abs ®
* let rec new_env = (f, V_closure(new_env,e1))::env
* in
* eval_ml_expr new_env e2
* ® raise Rec_def
* end
* Ref e ® let v = eval_ml_expr env e in V_ref (ref v)
* ;;
* eval_ml_expr : (string * ml_val) list ® ml_expr ® ml_val = áfunñ La seule "ruse" est l'introduction d'une déclaration récursive sur une valeur non fonctionnelle pour la construction de l'environnement d'une fermeture déclarée dans un let rec. Une autre possibilité serait d'effectuer un effet de bord à la construction de cet environnement où la variable f apparaitrait une premi`ere fois de manière factice dans l'environnement de la fermeture pour ensuite changer de valeur une fois le nouvel environnement construit. Il faut alors changer le type des environnements et en faire des ref.

On définit un environnement initial cohérent avec l'environnement de typage.

let initial_eval_env =
* let make_new_unop name = name, V_closure([  ], Abs(``x'', Unop ((``_ML_''^name),Var ``x''))) in
* let new_unop = map make_new_unop [ ``hd''``tl''``fst''``snd'' ]
* and s_env =
* [ 
* ``true'', V_const(V_bool true)
* ``false'', V_const(V_bool false)
*  ]
* in ref (new_unop@s_env);;
* initial_eval_env
* : (string * ml_val) list ref
* = ref [ ``hd'', V_closure ([  ], Abs (``x'', Unop (``_ML_hd'', Var ``x'')))
* ``tl'', V_closure ([  ], Abs (``x'', Unop (``_ML_tl'', Var ``x'')))
* ``fst'', V_closure ([  ], Abs (``x'', Unop (``_ML_fst'', Var ``x'')))
* ``snd'', V_closure ([  ], Abs (``x'', Unop (``_ML_snd'', Var ``x'')))
* ``true'', V_const (V_bool true) ``false'', V_const (V_bool false) ]

Il reste juste ensuite à embaler l'ensemble pour commencer à calculer...
let type_check_and_eval  e = 
  let t = typing_handler type_expr !initial_typing_env e 
  in 
    let v = eval_ml_expr !initial_eval_env e 
    in 
    let qt = snd(hd(generalize_types !initial_typing_env ["it",t]))
    in 
      v,qt
;;

Précédent Index Suivant