correzioni coppo
This commit is contained in:
parent
048affd516
commit
67ae30a28d
2 changed files with 72 additions and 89 deletions
BIN
tesi/tesi.pdf
BIN
tesi/tesi.pdf
Binary file not shown.
|
@ -1,5 +1,4 @@
|
|||
\begin{comment}
|
||||
TODO: neg is parsed incorrectly
|
||||
TODO: chiedi a Gabriel se T e S vanno bene, ma prima controlla che siano coerenti
|
||||
* TODO Scaletta [1/6]
|
||||
- [X] Introduction
|
||||
|
@ -103,6 +102,10 @@ into simple control flow structures such as \texttt{if, switch}, for example:
|
|||
| x::[] -> (1, Some x)
|
||||
| _::y::_ -> (2, Some y)
|
||||
\end{lstlisting}
|
||||
Given as input to the pattern matching compiler, this snippet of code
|
||||
gets translated into the Lambda intermediate representation of
|
||||
the OCaml compiler. The Lambda representation of a program is shown by
|
||||
calling the \texttt{ocamlc} compiler with \texttt{-drawlambda} flag.
|
||||
\begin{lstlisting}
|
||||
(if scrutinee
|
||||
(let (field_1 =a (field 1 scrutinee))
|
||||
|
@ -115,12 +118,6 @@ into simple control flow structures such as \texttt{if, switch}, for example:
|
|||
(makeblock 0 1 (makeblock 0 y)))))
|
||||
[0: 0 0a])
|
||||
\end{lstlisting}
|
||||
\begin{comment}
|
||||
%% TODO: side by side
|
||||
\end{comment}
|
||||
The code on the right is in the Lambda intermediate representation of
|
||||
the OCaml compiler. The Lambda representation of a program is shown by
|
||||
calling the \texttt{ocamlc} compiler with \texttt{-drawlambda} flag.
|
||||
|
||||
The OCaml pattern matching compiler is a critical part of the OCaml compiler
|
||||
in terms of correctness because bugs typically would result in wrong code
|
||||
|
@ -151,6 +148,7 @@ execution.
|
|||
Here are the decision trees for the source and target example program.
|
||||
|
||||
\begin{minipage}{0.5\linewidth}
|
||||
\scriptsize
|
||||
\begin{verbatim}
|
||||
Switch(Root)
|
||||
/ \
|
||||
|
@ -166,21 +164,22 @@ Here are the decision trees for the source and target example program.
|
|||
\end{verbatim}
|
||||
\end{minipage}
|
||||
\hfill
|
||||
\begin{minipage}{0.5\linewidth}
|
||||
\begin{minipage}{0.4\linewidth}
|
||||
\scriptsize
|
||||
\begin{verbatim}
|
||||
Switch(Root)
|
||||
/ \
|
||||
(= int 0) (!= int 0)
|
||||
/ \
|
||||
Leaf Switch(Root.1)
|
||||
(makeblock 0 / \
|
||||
(mkblock 0 / \
|
||||
0 0a) / \
|
||||
(= int 0) (!= int 0)
|
||||
/ \
|
||||
Leaf Leaf
|
||||
[x = Root.0] [y = Root.1.0]
|
||||
(makeblock 0 (makeblock 0
|
||||
1 (makeblock 0 x)) 2 (makeblock 0 y))
|
||||
(mkblock 0 (mkblock 0
|
||||
1 (mkblock 0 x)) 2 (mkblock 0 y))
|
||||
\end{verbatim}
|
||||
\end{minipage}
|
||||
|
||||
|
@ -207,7 +206,7 @@ reduced tree is equivalent to $C_i$.
|
|||
\subsection{From source programs to decision trees}
|
||||
Our source language supports integers, lists, tuples and all algebraic
|
||||
datatypes. Patterns support wildcards, constructors and literals,
|
||||
or-patterns $(p_1 | p_2)$ and pattern variables. We also support
|
||||
Or-patterns such as $(p_1 | p_2)$ and pattern variables. We also support
|
||||
\texttt{when} guards, which are interesting as they introduce the
|
||||
evaluation of expressions during matching. Decision trees have nodes
|
||||
of the form:
|
||||
|
@ -742,14 +741,10 @@ respectively the /precondition/ and the /postcondition/ that
|
|||
constitute the assertions of the program. C is the directive being
|
||||
executed.
|
||||
The language of the assertions P is:
|
||||
#+BEGIN_SRC
|
||||
P ::= true | false | a < b | P_{1} \wedge P_{2} | P_{1} \lor P_{2} | \notP
|
||||
#+END_SRC
|
||||
| P ::= true \vert false \vert a < b \vert P_{1} \wedge P_{2} \vert P_{1} \lor P_{2} \vert \not P
|
||||
where a and b are numbers.
|
||||
In the Hoare rules assertions could also take the form of
|
||||
#+BEGIN_SRC
|
||||
P ::= \forall i. P | \exists i. P | P_{1} \Rightarrow P_{2}
|
||||
#+END_SRC
|
||||
| P ::= \forall i. P \vert \exists i. P \vert P_{1} \Rightarrow P_{2}
|
||||
where i is a logical variable, but assertions of these kinds increases
|
||||
the complexity of the symbolic engine.
|
||||
Execution follows the rules of Hoare logic:
|
||||
|
@ -775,14 +770,14 @@ Execution follows the rules of Hoare logic:
|
|||
- Conditional :
|
||||
\begin{mathpar}
|
||||
\infer { \{P \wedge b \} c_1 \{Q\}, \{P\wedge\not b\}c_2\{Q\} }
|
||||
{ \{P\}if b then c_1 else c_2\{Q\} }
|
||||
{ \{P\}\text{if b then $c_1$ else $c_2$}\{Q\} }
|
||||
\end{mathpar}
|
||||
|
||||
- Loop : {P} is the loop invariant. After the loop is finished /P/
|
||||
holds and ¬̸b caused the loop to end.
|
||||
\begin{mathpar}
|
||||
\infer { \{P \wedge b \}c\{P\} }
|
||||
{ \{P\} while b do c \{P \wedge \neg b\} }
|
||||
{ \{P\}\text{while b do c} \{P \wedge \neg b\} }
|
||||
\end{mathpar}
|
||||
|
||||
Even if the semantics of symbolic execution engines are well defined,
|
||||
|
@ -816,7 +811,7 @@ existing translators that consists of taking the source and the target
|
|||
|
||||
** Source program
|
||||
The algorithm takes as its input a source program and translates it
|
||||
into an algebraic data structure called /decision_tree/.
|
||||
into an algebraic data structure which type we call /decision_tree/.
|
||||
|
||||
#+BEGIN_SRC
|
||||
type decision_tree =
|
||||
|
@ -824,7 +819,7 @@ type decision_tree =
|
|||
| Failure
|
||||
| Leaf of source_expr
|
||||
| Guard of source_blackbox * decision_tree * decision_tree
|
||||
| Node of accessor * (constructor * decision_tree) list * decision_tree
|
||||
| Switch of accessor * (constructor * decision_tree) list * decision_tree
|
||||
#+END_SRC
|
||||
|
||||
Unreachable, Leaf of source_expr and Failure are the terminals of the three.
|
||||
|
@ -842,18 +837,16 @@ Let's consider some trivial examples:
|
|||
function true -> 1
|
||||
#+END_SRC
|
||||
|
||||
[ ] Converti a disegni
|
||||
|
||||
Is translated to
|
||||
|Node ([(true, Leaf 1)], Failure)
|
||||
is translated to
|
||||
|Switch ([(true, Leaf 1)], Failure)
|
||||
while
|
||||
#+BEGIN_SRC
|
||||
function
|
||||
true -> 1
|
||||
| false -> 2
|
||||
#+END_SRC
|
||||
will give
|
||||
|Node ([(true, Leaf 1); (false, Leaf 2)])
|
||||
will be translated to
|
||||
|Switch ([(true, Leaf 1); (false, Leaf 2)])
|
||||
|
||||
It is possible to produce Unreachable examples by using
|
||||
refutation clauses (a "dot" in the right-hand-side)
|
||||
|
@ -864,7 +857,7 @@ true -> 1
|
|||
| _ -> .
|
||||
#+END_SRC
|
||||
that gets translated into
|
||||
Node ([(true, Leaf 1); (false, Leaf 2)], Unreachable)
|
||||
Switch ([(true, Leaf 1); (false, Leaf 2)], Unreachable)
|
||||
|
||||
We trust this annotation, which is reasonable as the type-checker
|
||||
verifies that it indeed holds.
|
||||
|
@ -875,20 +868,21 @@ branches, one that is taken in case the guard evaluates to true and
|
|||
the other one that contains the path taken when the guard evaluates to
|
||||
true.
|
||||
|
||||
[ ] Finisci con Node
|
||||
\begin{comment}
|
||||
[ ] Finisci con Switch
|
||||
[ ] Spiega del fallback
|
||||
[ ] rivedi compilazione per tenere in considerazione il tuo albero invece che le Lambda
|
||||
[ ] Specifica che stesso algoritmo usato per compilare a lambda, piu` optimizations
|
||||
\end{comment}
|
||||
|
||||
The source code of a pattern matching function has the
|
||||
following form:
|
||||
|
||||
|match variable with
|
||||
|\vert pattern₁ -> expr₁
|
||||
|\vert pattern₂ when guard -> expr₂
|
||||
|\vert pattern₃ as var -> expr₃
|
||||
|\vert pattern₁ \to expr₁
|
||||
|\vert pattern₂ when guard \to expr₂
|
||||
|\vert pattern₃ as var \to expr₃
|
||||
|⋮
|
||||
|\vert pₙ -> exprₙ
|
||||
|\vert pₙ \to exprₙ
|
||||
|
||||
and can include any expression that is legal for the OCaml compiler,
|
||||
such as /when/ guards and assignments. Patterns could or could not
|
||||
|
@ -896,45 +890,34 @@ be exhaustive.
|
|||
|
||||
Pattern matching code could also be written using the more compact form:
|
||||
|function
|
||||
|\vert pattern₁ -> expr₁
|
||||
|\vert pattern₂ when guard -> expr₂
|
||||
|\vert pattern₃ as var -> expr₃
|
||||
|\vert pattern₁ \to expr₁
|
||||
|\vert pattern₂ when guard \to expr₂
|
||||
|\vert pattern₃ as var \to expr₃
|
||||
|⋮
|
||||
|\vert pₙ -> exprₙ
|
||||
|\vert pₙ \to exprₙ
|
||||
|
||||
|
||||
This BNF grammar describes formally the grammar of the source program:
|
||||
|
||||
#+BEGIN_SRC bnf
|
||||
start ::= "match" id "with" patterns | "function" patterns
|
||||
patterns ::= (pattern0|pattern1) pattern1+
|
||||
;; pattern0 and pattern1 are needed to distinguish the first case in which
|
||||
;; we can avoid writing the optional vertical line
|
||||
pattern0 ::= clause
|
||||
pattern1 ::= "|" clause
|
||||
clause ::= lexpr "->" rexpr
|
||||
|
||||
lexpr ::= rule (ε|condition)
|
||||
rexpr ::= _code ;; arbitrary code
|
||||
|
||||
rule ::= wildcard|variable|constructor_pattern|or_pattern ;;
|
||||
|
||||
;; rules
|
||||
wildcard ::= "_"
|
||||
variable ::= identifier
|
||||
constructor_pattern ::= constructor (rule|ε) (assignment|ε)
|
||||
|
||||
constructor ::= int|float|char|string|bool
|
||||
|unit|record|exn|objects|ref
|
||||
|list|tuple|array
|
||||
|variant|parameterized_variant ;; data types
|
||||
|
||||
or_pattern ::= rule ("|" wildcard|variable|constructor_pattern)+
|
||||
|
||||
condition ::= "when" bexpr
|
||||
assignment ::= "as" id
|
||||
bexpr ::= _code ;; arbitrary code
|
||||
#+END_SRC
|
||||
| start ::= "match" id "with" patterns \vert{} "function" patterns
|
||||
| patterns ::= (pattern0\vert{}pattern1) pattern1+
|
||||
| ;; pattern0 and pattern1 are needed to distinguish the first case in which
|
||||
| ;; we can avoid writing the optional vertical line
|
||||
| pattern0 ::= clause
|
||||
| pattern1 ::= "\vert" clause
|
||||
| clause ::= lexpr "->" rexpr
|
||||
| lexpr ::= rule (ε\vert{}condition)
|
||||
| rexpr ::= _code ;; arbitrary code
|
||||
| rule ::= wildcard\vert{}variable\vert{}constructor_pattern\vert{}or_pattern ;;
|
||||
| ;; rules
|
||||
| wildcard ::= "_"
|
||||
| variable ::= identifier
|
||||
| constructor_pattern ::= constructor (rule\vert{}ε) (assignment\vert{}ε)
|
||||
| constructor ::= int\vert{}float\vert{}char\vert{}string\vert{}bool \vert{}unit\vert{}record\vert{}exn\vert{}objects\vert{}ref \vert{}list\vert{}tuple\vert{}array\vert{}variant\vert{}parameterized_variant ;; data types
|
||||
| or_pattern ::= rule ("\vert{}" wildcard\vert{}variable\vert{}constructor_pattern)+
|
||||
| condition ::= "when" bexpr
|
||||
| assignment ::= "as" id
|
||||
| bexpr ::= _code ;; arbitrary code
|
||||
|
||||
\begin{comment}
|
||||
Check that it is still coherent to this bnf
|
||||
|
@ -948,7 +931,7 @@ Patterns are of the form
|
|||
| c(p₁,p₂,...,pₙ) | constructor pattern |
|
||||
| (p₁\vert p₂) | or-pattern |
|
||||
|
||||
During compilation by the translators expressions are compiled into
|
||||
During compilation by the translators, expressions are compiled into
|
||||
Lambda code and are referred as lambda code actions lᵢ.
|
||||
|
||||
The entire pattern matching code is represented as a clause matrix
|
||||
|
@ -970,7 +953,7 @@ following rules apply
|
|||
|--------------------+---+--------------------+-------------------------------------------|
|
||||
| _ | ≼ | v | ∀v |
|
||||
| x | ≼ | v | ∀v |
|
||||
| (p₁ \vert\ p₂) | ≼ | v | iff p₁ ≼ v or p₂ ≼ v |
|
||||
| (p₁ \vert p₂) | ≼ | v | iff p₁ ≼ v or p₂ ≼ v |
|
||||
| c(p₁, p₂, ..., pₐ) | ≼ | c(v₁, v₂, ..., vₐ) | iff (p₁, p₂, ..., pₐ) ≼ (v₁, v₂, ..., vₐ) |
|
||||
| (p₁, p₂, ..., pₐ) | ≼ | (v₁, v₂, ..., vₐ) | iff pᵢ ≼ vᵢ ∀i ∈ [1..a] |
|
||||
|--------------------+---+--------------------+-------------------------------------------|
|
||||
|
@ -1038,14 +1021,14 @@ Similarly, all the right-hand-side expressions are of the form
|
|||
\texttt{observe <arg> <arg> ...} with the same constraints on arguments.
|
||||
|
||||
#+BEGIN_SRC
|
||||
type t = Z | S of t
|
||||
type t = K1 | K2 of t (* declaration of an algebraic and recursive datatype t *)
|
||||
|
||||
let _ = function
|
||||
| Z -> observe 0
|
||||
| S Z -> observe 1
|
||||
| S x when guard x -> observe 2
|
||||
| S (S x) as y when guard x y -> observe 3
|
||||
| S _ -> observe 4
|
||||
| K1 -> observe 0
|
||||
| K2 K1 -> observe 1
|
||||
| K2 x when guard x -> observe 2
|
||||
| K2 (K2 x) as y when guard x y -> observe 3
|
||||
| K2 _ -> observe 4
|
||||
#+END_SRC
|
||||
|
||||
Once parsed, the type declarations and the list of clauses are encoded in the form of a matrix
|
||||
|
@ -1069,7 +1052,7 @@ p_{m,1} & p_{m,2} & \cdots & p_{m,n} → lₘ)
|
|||
\end{equation*}
|
||||
|
||||
The base case C₀ of the algorithm is the case in which the $\vec{x}$
|
||||
is empty, that is $\vec{x}$ = (), then the result of the compilation
|
||||
is empty and the result of the compilation
|
||||
C₀ is l₁
|
||||
\begin{equation*}
|
||||
C₀((),
|
||||
|
@ -1102,7 +1085,7 @@ following four rules:
|
|||
\end{equation*}
|
||||
|
||||
That means in every lambda action lᵢ there is a binding of x₁ to the
|
||||
variable that appears on the pattern $p_{i,1}. Bindings are omitted
|
||||
variable that appears on the pattern p_{i,1}. Bindings are omitted
|
||||
for wildcard patterns and the lambda action lᵢ remains unchanged.
|
||||
|
||||
2) Constructor rule: if all patterns in the first column of P are
|
||||
|
@ -1344,7 +1327,7 @@ possible input values /S/. The fallback is not needed when the user
|
|||
doesn't use a wildcard pattern.
|
||||
%%% Give example of thing
|
||||
|
||||
| Cₛ ::= Leaf bb | Node(a, (Kᵢ → Cᵢ)^{i∈S} , C?)
|
||||
| Cₛ ::= Leaf bb | Switch(a, (Kᵢ → Cᵢ)^{i∈S} , C?)
|
||||
| a ::= Here | n.a
|
||||
| vₛ ::= K(vᵢ)^{i∈I} | n ∈ ℕ
|
||||
|
||||
|
@ -1492,11 +1475,11 @@ We prove this by induction on Cₜ:
|
|||
That means that the result of trimming on a Leaf is the Leaf itself
|
||||
b. The same applies to Failure terminal
|
||||
| Failure_{/a→π}(v) = Failure(v)
|
||||
c. When Cₜ = Node(b, (π→Cᵢ)ⁱ)_{/a→π} then
|
||||
c. When Cₜ = Switch(b, (π→Cᵢ)ⁱ)_{/a→π} then
|
||||
we look at the accessor /a/ of the subtree Cᵢ and
|
||||
we define πᵢ' = πᵢ if a≠b else πᵢ∩π
|
||||
Trimming a switch node yields the following result:
|
||||
| Node(b, (π→Cᵢ)ⁱ)_{/a→π} := Node(b, (π'ᵢ→C_{i/a→π})ⁱ)
|
||||
| Switch(b, (π→Cᵢ)ⁱ)_{/a→π} := Switch(b, (π'ᵢ→C_{i/a→π})ⁱ)
|
||||
|
||||
\begin{comment}
|
||||
Actually in the proof.org file I transcribed:
|
||||
|
@ -1507,10 +1490,10 @@ This is not correct because you don't have Unreachable nodes in target decision
|
|||
For the trimming lemma we have to prove that running the value vₜ against
|
||||
the decistion tree Cₜ is the same as running vₜ against the tree
|
||||
C_{trim} that is the result of the trimming operation on Cₜ
|
||||
| Cₜ(vₜ) = C_{trim}(vₜ) = Node(b, (πᵢ'→C_{i/a→π})ⁱ)(vₜ)
|
||||
| Cₜ(vₜ) = C_{trim}(vₜ) = Switch(b, (πᵢ'→C_{i/a→π})ⁱ)(vₜ)
|
||||
We can reason by first noting that when vₜ∉(b→πᵢ)ⁱ the node must be a Failure node.
|
||||
In the case where ∃k| vₜ∈(b→πₖ) then we can prove that
|
||||
| C_{k/a→π}(vₜ) = Node(b, (πᵢ'→C_{i/a→π})ⁱ)(vₜ)
|
||||
| C_{k/a→π}(vₜ) = Switch(b, (πᵢ'→C_{i/a→π})ⁱ)(vₜ)
|
||||
because when a ≠ b then πₖ'= πₖ and this means that vₜ∈πₖ'
|
||||
while when a = b then πₖ'=(πₖ∩π) and vt∈πₖ' because:
|
||||
- by the hypothesis, vₜ∈π
|
||||
|
@ -1580,8 +1563,8 @@ I think the unreachable case should go at the end.
|
|||
|equiv(S, Failure, Failure) := Yes
|
||||
Given that ∀v, Failure(v) = Failure, the statement holds.
|
||||
3. When we have a Leaf or a Failure at the left side:
|
||||
| equiv(S, Failure as Cₛ, Node(a, (πᵢ → Cₜᵢ)ⁱ)) := Forall(equiv( S∩a→π(kᵢ)), Cₛ, Cₜᵢ)ⁱ)
|
||||
| equiv(S, Leaf bbₛ as Cₛ, Node(a, (πᵢ → Cₜᵢ)ⁱ)) := Forall(equiv( S∩a→π(kᵢ)), Cₛ, Cₜᵢ)ⁱ)
|
||||
| equiv(S, Failure as Cₛ, Switch(a, (πᵢ → Cₜᵢ)ⁱ)) := Forall(equiv( S∩a→π(kᵢ)), Cₛ, Cₜᵢ)ⁱ)
|
||||
| equiv(S, Leaf bbₛ as Cₛ, Switch(a, (πᵢ → Cₜᵢ)ⁱ)) := Forall(equiv( S∩a→π(kᵢ)), Cₛ, Cₜᵢ)ⁱ)
|
||||
The algorithm either returns Yes for every sub-input space Sᵢ := S∩(a→π(kᵢ)) and
|
||||
subtree Cₜᵢ
|
||||
| equiv(Sᵢ, Cₛ, Cₜᵢ) = Yes ∀i
|
||||
|
@ -1592,11 +1575,11 @@ I think the unreachable case should go at the end.
|
|||
| vₛ≃vₜ∈S ∧ Cₛ(vₛ)≠Cₜ(vₜ)
|
||||
we can say that
|
||||
| equiv(Sᵢ, Cₛ, Cₜᵢ) = No(vₛ, vₜ) for some minimal k∈I
|
||||
4. When we have a Node on the right we define πₙ as the domain of
|
||||
4. When we have a Switch on the right we define πₙ as the domain of
|
||||
values not covered but the union of the constructors kᵢ
|
||||
| πₙ = ¬(⋃π(kᵢ)ⁱ)
|
||||
The algorithm proceeds by trimming
|
||||
| equiv(S, Node(a, (kᵢ → Cₛᵢ)ⁱ, C_{sf}), Cₜ) :=
|
||||
| equiv(S, Switch(a, (kᵢ → Cₛᵢ)ⁱ, C_{sf}), Cₜ) :=
|
||||
| Forall(equiv( S∩(a→π(kᵢ)ⁱ), Cₛᵢ, C_{t/a→π(kᵢ)})ⁱ +++ equiv(S∩(a→π(kᵢ)), Cₛ, C_{a→πₙ}))
|
||||
The statement still holds and we show this by first analyzing the
|
||||
/Yes/ case:
|
||||
|
|
Loading…
Reference in a new issue