open Domains;;

exception TypeErrorInOperation;;

let eval_operation operation value1 value2 =
  match operation, value1, value2 with
    | OPlus, (VEntier i1), (VEntier i2) ->
      VEntier(i1 + i2)
    | OMoins, (VEntier i1), (VEntier i2)  ->
      VEntier(i1 - i2)
    | OFois, (VEntier i1), (VEntier i2)  ->
      VEntier(i1 * i2)
    | ODivise, (VEntier i1), (VEntier i2)  ->
      VEntier(i1 / i2)
    | OEgal, (VEntier i1), (VEntier i2)  ->
      VBooleen(i1 = i2)
    | OMineur, (VEntier i1), (VEntier i2)  ->
      VBooleen(i1 < i2)
    | OEt, (VBooleen b1), (VBooleen b2)  ->
      VBooleen(b1 && b2)
    | OOu, (VBooleen b1), (VBooleen b2)  ->
      VBooleen(b1 || b2)
    | _ ->
      raise TypeErrorInOperation;; (* impossible for well-typed programs *)

let rec eval_expression expression local_environment global_environment =
  match expression with
  | ELitteral c ->
    (match c with
      | LEntier i -> VEntier i
      | LBooleen b -> VBooleen b)
  | EVariable x ->
    Environment.lookup
      x
      (Environment.extend_with_environment global_environment local_environment)
  | EOperation(e1, op, e2)->
    eval_operation
      op
      (eval_expression e1 local_environment global_environment)
      (eval_expression e2 local_environment global_environment)
  | EFonction(x, tau, e) ->
    VFonction(local_environment, x, e)
  | EAppel(e1, e2) ->
    let VFonction(rho_c, x_c, e_c) =
      eval_expression e1 local_environment global_environment in
    let c2 =
      eval_expression e2 local_environment global_environment in
    eval_expression e_c (Environment.extend_with_binding x_c c2 rho_c) global_environment
  | ESi(e1, e2, e3) ->
    let c1 = eval_expression e1 local_environment global_environment in
    if c1 = VBooleen true then
      eval_expression e2 local_environment global_environment
    else
      eval_expression e3 local_environment global_environment
  | ESoit(x, tau, e1, e2) ->
    let c1 = eval_expression e1 local_environment global_environment in
    eval_expression
      e2
      (Environment.extend_with_binding x c1 local_environment)
      global_environment;;
