(*************************************************)
(* Interpréteur en appel par nom, qui utilse     *)
(* non pas des glaçons comme ceux du cours, mais *)
(* des glaçons du langage hôte.                  *)
(*************************************************)

(* Source 
inter_cbn.ml *)

(***********************************)
(* Signature du module des glaçons *)
(***********************************)


module type Thunk = sig

  type 'a t

  val create : (unit -> 'a) -> 'a t
  (** Créer un glaçon.

     À cause de l'appel par valeur de Caml
     il est impossible de passer le terme (Caml) directement.
     Par exemple :
       create (fun () -> fib 20)
     et non  pas :
       create (fib 20).

    Pour ce qui est de l'intepréteur, c'est l'appel récursif à
    l'intepréteur qui sera retardé. Voir ici.
  **)


  val force : 'a t -> 'a
  (* Dégeler un glaçon *)
end

(********************************************************)
(* Glaçons codés comme des fonctions de type unit -> 'a *)
(********************************************************)


module Thunk : Thunk = struct

  type 'a t = unit -> 'a

  let create f = f

  let force f = f ()

end

(***********************************************************)
(* Glaçons de la  bibliothèque Caml                        *)
(* Ils ont la propriété que le premier force               *)
(* dégèle le glaçon et remplace le glacon par le résultat. *)
(* L'effet est que le calcul du résultat est effectué au   *)
(* plus une fois. Voir 
Lazy.                               *)
(***********************************************************)


module StdLazy : Thunk = struct

  type 'a t = 'a Lazy.t

  let create f = lazy (f ())

  let force = Lazy.force

end

(*************************************************************)
(* Et d'ailleurs voici un  module MyLazy qui fait à peu près *)
(* la même chose à l'aide des références de Caml.            *)
(*************************************************************)


module
 MyLazy : Thunk = struct

  type 'a intern = Forced of 'a | NotForced of (unit -> 'a)
  (* État interne *)

  type 'a t = ('a internref

  let create f = ref (NotForced f)

  let force thk = match !thk with
  | Forced v -> v
  | NotForced f ->
      let v = f () in
      thk := Forced v ;
      v

end

(********************************************************************)
(* Interpréteur proprement dit, paramétré par un module des glaçons *)
(********************************************************************)



module
 Make(Thunk:Thunk) = struct

  open Pcf
  open S
  open Ast

  type value = Num_v of int | Clo_v of string * t * env
  and thunk =  value Thunk.t
  and env = (string * thunklist

  open Printf

  let print_value chan v = match v with
  | Num_v i -> fprintf chan "%i" i
  | Clo_v _ -> fprintf chan "<fun>"

  exception Error of string

  let find env x =
    try List.assoc x env
    with Not_found -> raise (Error ("variable: "^x^" undefined"))

  let rec inter env t = match t with
  (* Les valeurs *)
  | Num i -> Num_v i
  | Fun (x,t) -> Clo_v (x,t,env)
  (* Dégeler le glaçon *)
  | Var x -> Thunk.force (find env x)
  (* Rien de bien remarquable *)
  | Op (op,t1,t2) ->
      let n1 = inter_int env t1 in
      let n2 = inter_int env t2 in
      Num_v (Op.to_fun op n1 n2)
  | Ifz (t1,t2,t3) ->
      let v1 = inter_int env t1 in
      inter env (if v1 = 0 then t2 else t3)
(* Noter que les arguments sont traités par une fct 'arg' *)
  | Let (x,t1,t2) ->
      inter ((x,arg env t1)::envt2
  | App (t1,t2) ->
      let x,t_clo,e_clo = inter_clo env t1 in
      inter ((x,arg env t2)::e_clot_clo
  | Fix (f,tas fix -> inter ((farg env fix)::envt

  and arg env t = match t with
(* Si l'argument est une variable, l'environnement
   lui associe déjà un glaçon. Inutile d'en fabriquer un autre. *)

  | Var x -> find env x
(* Toute l'astuce est ici.
   Le glaçon encapsule un appel à l'interpréteur, qui ne sera
   effectué que lorsque le glaçon est dégelé. *)

  | _ -> Thunk.create (fun () -> inter env t)

  and inter_int env t = match inter env t with
  | Num_v i -> i
  | Clo_v _ -> raise (Error "Int expected, got Clo")

  and inter_clo env t = match inter env t with
  | Clo_v (x,t,e) -> (x,t,e)
  | Num_v _ -> raise (Error "Clo expected, got Int")

  let print_thunk chan thk = print_value chan (Thunk.force thk)

  let loop () =
    S.Top.loop print_thunk
      (fun env t ->
        let v = inter env t in
        Thunk.create (fun () -> v))

end



module
 Inter = Make(MyLazy)
(* On peut remplacer MyLazy ci-dessus par Thunk ou StdLazy. *)

let () = Inter.loop ()

This document was translated from LATEX by HEVEA.