(* Source machine.ml *)
open S.Ast
open
 Code (* Documentation de Code *)

(* La réalisation de la machine elle-même
   est  un peu plus loin *)


(* Les valeurs de la machines sont des entiers ou des fermetures *)
type value = Int of int | Clo of code * env
and
 env = value list

(* Il faut aussi pouvoir stocker environnements et code sur la pile. *)
type stack_elt = Val of value | Env of env | Code of code

exception
 Error

(**************************************)
(* Afficheur de l'état de la machine, *)
(* bien utile pour debugger.          *)
(**************************************)

open Printf

(* Affiche au plus deux éléments d'une liste *)
let plist p chan = function
| []  -> ()
| [x] -> p chan x
| [x;y] -> fprintf chan "%a; %a" p x p y
x::y::_ -> fprintf chan "%a; %a; ..." p x p y

let
 pins chan i = Code.print_instruction chan i

(* Code *)
let pcode chan c = plist pins chan c

(* Affiche une valeur *)
let rec pval chan = function
Int i -> fprintf chan "%i" i
Clo (c,eas clo ->  fprintf chan "<%a * %a>" pcode c (penv_rec cloe

(* Affiche un elt de pile *)
and  pstack_elt chan = function
Val v -> pval chan v
Env e -> fprintf chan "<env>[%a]" penv e
Code c -> fprintf chan "<code>[%a]" pcode c

(* Piles et environnements *)
and pstack chan s = plist pstack_elt chan s

and
 penv chan e = plist pval chan e

(* Pour éviter de boucler sur les env. récursifs des fermetures *)
and penv_rec clo chan e = match e with
| [] -> ()
x::e ->
  if x == clo then match e with
  | [] -> fprintf chan "%s" "<rec>"
  | _::_ -> fprintf chan "<rec>; %a" penv e
  else
    penv chan (x::e)

(* Afficher un état (A,S,E,C) *)
let pstate chan a s e c =
  fprintf chan "<%a, [%a], [%a], [%a]>\n"
    pval a
    pstack s
    penv e
    pcode c

let
 debug =
  List.exists
    (fun s -> s = "-v")
    (Array.to_list Sys.argv)

(*****************************************)
(* Machine proprement dite, cf. le cours *)
(*****************************************)

let rec run a s e c =
  if debug then begin
    pstate stderr a s e c ;
    flush stderr
  end ;
  match a,s,c with
(* Facile *)
  | _,_,Ldi i::c -> run (Int is e c
  | _,_,Push::c  -> run a (Val a::se c
(* Plus général que le cours qui ne donne que l'exemple de l'addition *)
  | Int n2,Val (Int n1)::s,IOp op::c -> (* Noter 'Val' et l'ordre n1/n2 *)
      run (Int (Op.to_fun op n1 n2)) s e c
(* Le Test sans concaténation de code.
   Cela revient à appeler une fonction sans argument,
   Cf Apply/Retour de fonction plus bas *)

  | Int 0,_,Test (c2,_)::c ->
      run a (Code c::Env e::se c2
  | Int _,_,Test (_,c3)::c ->
      run a (Code c::Env e::se c3
(* Code avec concaténation
  | Int 0,_,Test (c2,_)::c ->
      run a s e (c2@c)
  | Int _,_,Test (_,c3)::c ->
      run a s e (c3@c)
*)
(* Facile *)

  | _,_,Extend::c -> run a s (a::ec
(* Facile, si on connaît List.nth *)
  | _,_,Search k::c -> run (List.nth e ks e c
(* Facile, noter l'application du constructeur 'Env' *)
  | _,_,Pushenv::c -> run a (Env e::se c
  | _,Env e::s,Popenv::c -> run a s e c
(* Fermeture ordinaires, une bête paire code X env *)
 | _,_,Mkclos cc::c ->
      run (Clo (cc,e)) s e c
(* Fermetures récursives, il faut s'ajouter en tête de l'env *)
  | _,_,Mkclosrec cc::c ->
      let rec clos = Clo (cc,clos::ein
      run clos s e c
(* Pas facile, voir le cours *)
  | Clo (cc,ce),Val v::s,Apply::c ->
      run a (Code c::Env e::s) (v::cecc
(* Retour de fonction : il y a un code et un env. sur la pile *)
  | a,(Code c::Env e::s),[] ->
      run a s e c
(* Fin le l'exécution du code : pile et code sont vides *)
  | a,[],[] -> a
(* Toutes les autres possibilités sont des erreurs à l'exécution *)
  |  _,_,_ -> raise Error

let
 exec env code = run (Int 0) [] env code

let
 print_value chan = function
  | Int i -> Printf.fprintf chan "%i" i
  | Clo _ -> output_string chan "<fun>"

This document was translated from LATEX by HEVEA.