TP1 - Sémantique dénotationnelle

The goal of this session is to implement an interpreter matching the denotational semantic.

Implement the missing function in this (recommended but not mandatory) order :

Numerical domains

  1. domains/

Implement the missing ffunctions parts unctions in the MakeConcrete functor.

First, find a suitable type t representing boolean values. Second, define all booleans operations on this type.

  1. domains/

Implement the missing functions in the MakeConcrete functor.

The integer operations returns t add_bottom * err. The first element is the result of the operation, Nb res, or Bot if the expression is undefined (e.g. division by zero). The second element, is the set of error raised in the evaluation of the expression.

The bin and bin_bool functions are auxiliary functions used for evaluating expressions that don’t raise any errors (add, sub, …).

The other operations that you need to define are:

  • unary operations uplusand uminus. They are straightforward.
  • binary operations div and modulo: you have to handle possible division by zero errors.

In the int_domain boolean predicates such as less return a couple of boolean. The first one corresponds to can_be_true the second one to can_be_false. For example, less should return true, false and less, false, true. And the error part should be E.empty in both case.

Value Domain

  1. domains/

First, ensure that you have a correct signature for both functor: if necessary, apply the modifications of this commit. (Sorry about that ☹️)

Implement the MakeValueBasics functor. The already implemented MakeValue functor, automatically lifts all operations (add, equal) by calling the right function with the corresponding arguments in ValueBasics. So you only have to implments the following functions:

  • int_bool_binary_bool: that lifts equality/inequality tests. i.e. operations of type α -> α -> bool where α is either I.t or B.t. Comparing two values of different types should raise a type error.
  • int_unary: for operation of type I.t -> I.t. (e.g. uminus)
  • int_binary: for operation of type I.t -> I.t -> I.t (e.g. add)
  • int_binary_bool: for operation of type I.t -> I.t -> B.t (e.g. less)
  • bool_unary: for operation of type B.t -> B.t (not)
  • bool_binary: for operation of type B.t -> B.t -> B.t (e.g. or)

Take a look at type ival. It denotes values with an int and a bool part. For each function, apply the function given in argument to the correct part and raise errors if the values don’t match the expected type. For this domain, don’t spend too much time to ensure well-typedness of values since it will be the subect of next week.


  1. domains/

Take a look at types simple_env and env. Understand what are their goals.

You have two functions to implement here :

  • eval_expr that returns the set of possible values for an expression. Feel free to define auxiliary functions.
  • eval_assign that handles an assignment.


  1. interpreter/

To conclude, you will need to implement the iterator for the denotational semantic.

lfp computes the fixpoint of the function f given in argument by iteration starting from pre-fixpoint x. The cmp argument is used to compare values to test if we reached a fixpoint. Since we use, Set.Make and Map.Make functors, make sure to use the compare functions of this modules and not the default compare function. Otherwise you can have imprecise results such as:

module ISet = Set.Make(Int)
let s1 = ISet.empty |> ISet.add 0 |> ISet.add 1
let s2 = ISet.empty |> ISet.add 1 |> ISet.add 0
(* Both sets should be equal to {0, 1} *)
let _ =
  Format.printf "compare s1 s2 = %i\n" (compare s1 s2);
  Format.printf " s1 s2 = %i\n" ( s1 s2);

eval_stat computes the denotational semantic of a statement in an environment.