Las expresiones se forman a partir de enteros y boleanos simples con la sintáxis de la figura 2.
C ::=
skip | X:= E | C1;C2 | if E then C1 else C2 | while E do C Figura 1: Sintáxis de los comandos
Se busca definir semánticas estáticas, operacionales naturales y axiomáticas para este lenguaje. Todas se representan en semántica natural por medio de la definición de una relación entre un programa y "valores"". Esta relación se describe con la ayuda de reglas de inferencia.
E ::=
Xs Variables tipadas | true | false Constantes boleanas | E1 xor E2 O exclusivo | n Constantes enteras | null E Test de nulidad de enteros | E1 op E2 Operación binaria sobre los enteros s ::= nat | bool op ::= + | - | * | ... Figura 2: Sintáxis de las expresiones
La relación de tipado E:s se define por los axiomas y reglas de la figura 3.
Para los comandos, se define la relación C: ok por las reglas de la figura 4.
Xs:s true:bool false:bool n:nat
E1:bool E2:bool E1 xor E2:bool
E:nat null E:bool
E1:nat E2:nat E1 op E2:nat Figura 3: Semántica estática de las expresiones
skip: ok
E:s X:= E: ok
C1 : ok C2 : ok C1;C2: ok
E:bool C1: ok C2: ok if E then C1 else C2: ok
E:bool C: ok while E do C:ok Figura 4: Semántica estática de los comandos
m|- Xs|>m(X,s) m|- true|> true m|- false|> false m|- n|> n
m|- E1|> b1 m|- E2|> b2 m|- E1 xor E2|> b1 xor b2
m|- E|> n m|- null E |> null n
m|- E1|> n1 m|- E2|> n2 m|- E1 op E2|> n1 op n2 Figura 5: Semántica de las expresiones
m |- skip |> m
m|- E |> v m |- X:= E |> m[X¬ v]
m|- C1 |> m1 m1|- C2 |> m' m|- C1 ; C2 |> m'
m|- E |> true m|- C1|> m' m|- if E then C1 else C2 |> m'
m|- E |> false m|- C2|> m' m|- if E then C1 else C2 |> m'
m|- E |> true m|- C|> m1 m1|-while E do C |> m' m|- while E do C |> m'
m|- E |> false m|- while E do C |> m Figure 6: Semática de los comandos
La conjunción y la implicación de dos predicados se escribirán P Ù Q y P Þ Q, si x es una variable y E una expresión, entonces P[x¬ E] representa el predicado que a cualquier memoria m le asocia P(m[x¬ v]) donde v es el valor tal que m |- E |> v. Si w es un valor, entonces E=w representa el predicado que a todo m le asocia la propiedad v=w donde v es el valor tal que m |- E |> v.
La definición de esta relación está dada por las reglas de inferencia de la figura 7.
{P}skip{P} {P[X¬ E]}X := E{P}
{P}C1{P1} {P1}C2{Q} {P}C1 ; C2{Q}
{P Ù (E=true )}C1{Q} {P}if E then C1 else C2{Q}
{P Ù (E=false )}C2{Q} {P}if E then C1 else C2{Q}
{P Ù (E=true )}C{P} {P}while E do C{ P Ù (E=false )}
P Þ P1 {P1}C{Q1} Q1Þ Q {P}C{Q} Figure 7: Semántica axiomática de los comandos
Coq < Variable name : Set.
Coq < Inductive sort : Set := Sbool : sort | Snat : sort.
Coq < Variable op : Set.
Coq < Inductive expr : Set :=
Coq < Var
: name -> sort -> expr
Coq < | Tr
: expr
Coq < | Fa
: expr
Coq < | Xor : expr
-> expr -> expr
Coq < | Num : nat
-> expr
Coq < | Null : expr ->
expr
Coq < | Opn : op
-> expr -> expr -> expr.
Coq < Inductive exprcorrect : sort -> expr -> Prop
Coq < := corvar : (n:name)(s:sort)(exprcorrect
s (Var n s))
Coq < | cortr
: (exprcorrect Sbool Tr)
Coq < | corfa
: (exprcorrect Sbool Fa)
Coq < | corxor :
(b,c:expr)(exprcorrect Sbool b)->(exprcorrect Sbool c)
Coq <
->(exprcorrect Sbool (Xor b c))
Coq < | cornum :
(n:nat)(exprcorrect Snat (Num n))
Coq < | cornull : (e:expr)(exprcorrect
Snat e)->(exprcorrect Sbool (Null e))
Coq < | corop
: (o:op)(e,f:expr)(exprcorrect Snat e)->(exprcorrect Snat f)
Coq <
->(exprcorrect Snat (Opn o e f)).
Coq < Inductive semval : Set :=
Coq < Bool : bool
-> semval
Coq < | Nat : nat
-> semval.
Coq < Definition boolxor : bool->bool->bool
Coq < := [b1,b2]if b1 then if b2 then
false else true else b2.
Coq < Definition iszero : nat->bool
Coq < := [n:nat]Cases n of O => true
| (S n) => false end.
Coq < Variable semop : op->nat->nat->nat.
Coq < Definition sort_val : semval -> sort
Coq < := [v:semval]Cases v of (Bool
b)=>Sbool | (Nat n)=>Snat end.
Se utilizará igualmente una relación compat entre los valores y las clases, tal que (compat Sbool v) sea equivalente a $ b:bool.v=(Bool b) (definida por el predicado valbool) y (compat Snat v) equivalente a $ n:nat.v=(Nat n) (definida por el predicado valnat).
Coq < Inductive valbool : semval->Prop
Coq < := valbool_intro : (b:bool)(valbool
(Bool b)).
Coq < Inductive valnat : semval->Prop
Coq < := valnat_intro : (n:nat)(valnat
(Nat n)).
Coq < Definition compat : sort -> semval -> Prop
Coq < := [s,v]Cases s of Sbool =>
(valbool v) | Snat => (valnat v) end.
Nótese que la definición inductiva de valbool es una codificación inductiva directa del predicado [x:semval](EX b:bool | x=(Bool b)). Se puede probar fácilmente la correspondencia entre compat y sort_val.
Coq < Theorem compat_sort_val
Coq <
: (s:sort)(v:semval)(compat s v)->s=(sort_val v).
Una alternativa podría ser la de representar la noción de compatibilidad por un predicado inductivo:
Coq < Inductive compat1 : sort -> semval -> Prop
:=
Coq < compat_bool : (b:bool)(compat1
Sbool (Bool b))
Coq < | compat_nat : (n:nat)(compat1 Snat (Nat
n)).
Las dos definiciones son equivalentes. Nótese que en el segundo caso, la propiedad
Coq < Definition memory := name->sort->semval.
Coq < Section Valexpr.
Coq < Variable memstate : memory.
Coq < Hypothesis memstate_correct : (n:name)(s:sort)(compat s (memstate n s)).
La definición exprval formaliza la semántica de las expresiones tal como se presentó en la figura 5.
Coq < Inductive exprval : expr -> semval -> Prop
:=
Coq < valvar : (n:name)(s:sort)(exprval
(Var n s) (memstate n s))
Coq < | valtr : (exprval Tr
(Bool true))
Coq < | valfa : (exprval Fa
(Bool false))
Coq < | valxor : (f,g:expr)(bf,bg:bool)
Coq <
(exprval f (Bool bf))->(exprval g (Bool bg))
Coq <
->(exprval (Xor f g) (Bool (boolxor bf bg)))
Coq < | valnum : (n:nat)(exprval (Num
n) (Nat n))
Coq < | valtzero : (f:expr)(nf:nat)(exprval f
(Nat nf))
Coq <
->(exprval (Null f) (Bool (iszero nf)))
Coq < | valopn : (o:op)(f,g:expr)(nf,ng:nat)
Coq <
(exprval f (Nat nf))->(exprval g (Nat ng))
Coq <
->(exprval (Opn o f g) (Nat (semop o nf ng))).
Ahora se puede enunciar el teorema que nos asegura que toda expresión correctamente tipada admite un valor.
Coq < Theorem expr_val_cor :
Coq < (E:expr)(s:sort)(exprcorrect
s E)->(EX v:semval | (exprval E v)).
La demostración por recurrencia necesita un lema más fuerte que diga que el valor calculado es compatible con la clase de la expresión.
Coq < Lemma expr_val_cor_dom :
Coq < (E:expr)(s:sort)(exprcorrect
s E)
Coq < ->(EX v:semval | (compat
s v) & (exprval E v)).
Se puede simplificar la representación haciendo implícita en la definición de la memoria la noción de compatibilidad.
Coq < Definition sval : sort -> Set
Coq < := [s]Cases s of Sbool
=> bool | Snat => nat end.
Coq < Definition memory1 := name->(s:sort)(sval s).
Se define entonces:
Coq < Variable memstate1 : memory1.
Coq < Inductive exprval1 : expr -> (s:sort)(sval
s) -> Prop :=
Coq < valvar1 : (n:name)(s:sort)(exprval1
(Var n s) s (memstate1 n s))
Coq < | valtr1 : (exprval1 Tr
Sbool true)
Coq < | valfa1 : (exprval1 Fa
Sbool false)
Coq < | valxor1 : (f,g:expr)(bf,bg:bool)
Coq <
(exprval1 f Sbool bf)->(exprval1 g Sbool bg)
Coq <
->(exprval1 (Xor f g) Sbool (boolxor bf bg))
Coq < | valnum1 : (n:nat)(exprval1
(Num n) Snat n)
Coq < | valtzero1 : (f:expr)(nf:nat)(exprval1
f Snat nf)
Coq <
->(exprval1 (Null f) Sbool (iszero nf))
Coq < | valopn1 : (o:op)(f,g:expr)(nf,ng:nat)
Coq <
(exprval1 f Snat nf)->(exprval1 g Snat ng)
Coq <
->(exprval1 (Opn o f g) Snat (semop o nf ng)).
El lema de corrección de la evaluación se enuncia entonces simplemente:
Coq < Theorem expr_val_cor1 :
Coq < (E:expr)(s:sort)(exprcorrect
s E)->(EX v:(sval s) | (exprval1 E s v)).
La formalización y la prueba son así más simples, sin embargo, el uso de los tipos dependientes hace que la formalización de la función de escritura sobre la memoria sean más complicada. En efecto, se debe tener con n:name,s:sort,v:(sval s),mem:memory1
Coq < Definition update : (m:nat)(s':sort)(sval s').
El simple hecho de saber que s=s' no basta para asegurar que el término v de tipo (sval s) es también de tipo (sval s'). Será necesario utilizar un análisis por casos según los valores de s y s'. Este análisis no hubiera sido posible si el conjunto de clases hubiera permanecido paramétrico.
De forma general, en presencia de los tipos dependientes, el uso de
la igualdad es delicado. No es posible, por ejemplo, escribir v=v'
con v:(sval s) y v':(sval
s') salvo cuando s y s' sean convertibles. La presencia
de una hipótesis s=s' es insuficiente. Será
preciso utilizar nociones más complejas de igualdad.
Coq < Inductive exprerr : expr -> Prop
Coq < := errxorl
: (b,c:expr)(exprcorrect Snat b)->(exprerr (Xor b c))
Coq < | errxorr
: (b,c:expr)(exprcorrect Snat c)->(exprerr (Xor b c))
Coq < | errtzero
: (b:expr)(exprcorrect Sbool b)->(exprerr (Null b))
Coq < | erropl
: (op:op)(b,c:expr)
Coq <
(exprcorrect Sbool b)->(exprerr (Opn op b c))
Coq < | erropr
: (op:op)(b,c:expr)
Coq <
(exprcorrect Sbool c)->(exprerr (Opn op b c))
Coq < | errcongxorl : (b,c:expr)(exprerr
b)->(exprerr (Xor b c))
Coq < | errcongxorr : (b,c:expr)(exprerr
c)->(exprerr (Xor b c))
Coq < | errcongtzero : (b:expr)(exprerr
b)->(exprerr (Null b))
Coq < | errcongopl : (op:op)(b,c:expr)(exprerr
b)->(exprerr (Opn op b c))
Coq < | errcongopr : (op:op)(b,c:expr)(exprerr
c)->(exprerr (Opn op b c)).
El teorema que expresa la decidibilidad del tipado y la evaluación es justamente una prueba del hecho de que para toda expresión E, o bien es posible construir un valor v de la expresión cuyo tipo es el de la expresión, o bien se puede probar que la expresión E está mal formada. El hecho de utilizar una noción constructiva de la disyunción y del existencial, asegura la existencia de un algoritmo que permita decidir en qué caso estamos y calcular efectivamente el valor. Se establece así un resultado de decibilidad sin tener que representar una noción de computabilidad.
Coq < Theorem expr_val_check_proof :
Coq < (E:expr){v:semval
| (exprval E v) & (exprcorrect (sort_val v) E)}
Coq <
+{exprerr E}.
Si nos centramos solamente en la parte de cálculo de esta prueba, se tendrá una función eval_prog que a toda expresión le asocia un valor o nada en absoluto. Esta función puede representarse igualmente en Gallina utilizando el tipo Exc de Coq análogo al tipo option de CAML.
Coq < Print Exc.
Inductive Exc [A:Set] : Set := value : A->(Exc
A) | error : (Exc A)
La función eval_prog se calcula mediante un punto fijo estructural sobre la expresión.
Coq < Fixpoint eval_prog [e:expr]:(Exc semval)
Coq < := Cases e of
Coq < (Var n s) => (value semval
(memstate n s))
Coq < | Tr
=> (value semval (Bool true))
Coq < | Fa
=> (value semval (Bool false))
Coq < |(Xor f g) => Cases (eval_prog
f) (eval_prog g) of
Coq <
(value (Bool bf)) (value (Bool bg)) =>
Coq <
(value semval (Bool (boolxor bf bg)))
Coq <
| _
_
=> (error semval)
Coq <
end
Coq < |(Num n) => (value
semval (Nat n))
Coq < |(Null f) => Cases (eval_prog
f) of
Coq <
(value (Nat nf)) => (value semval (Bool (iszero nf)))
Coq <
| _
=> (error semval)
Coq <
end
Coq < |(Opn o f g) => Cases (eval_prog
f) (eval_prog g) of
Coq <
(value (Nat nf)) (value (Nat ng)) =>
Coq <
(value semval (Nat (semop o nf ng)))
Coq <
| _
_ =>
(error semval)
Coq <
end
Coq < end.
Coq < End Valexpr.
Cases (fdec a b) of (left _) => p | (right _) => q end.O de forma equivalente:
(ifdec (fdec a b) p q).Se olvida la información de la prueba para construir la expresión pero podrá ser fácilmente utilizada cuando se trate de demostrar propiedades de esta expresión.
Comenzamos por declarar el axioma de decidibilidad de la igualdad sobre los nombres y probaremos la decidibilidad de la igualdad sobre el conjunto de clases.
Coq < Variable eq_name_dec : (n,m:name){n=m}+{~n=m}.
Coq < Theorem eq_sort_dec : (s,s':sort){s=s'}+{~s=s'}.
Podemos definir ahora la operación update de actualización de la memoria que se hará en una sección introduciendo el nombre de la variable x, el valor que le queremos asignar v y la memoria m. La clase del valor se denota localmente por s.
Coq < Section Update.
Coq < Variable x : name.
Coq < Variable v : semval.
Coq < Variable mem : memory.
Coq < Local s := (sort_val v).
Coq < Definition update : memory
Coq < := [m:name][s':sort]
Coq < (ifdec (eq_sort_dec
s s')
Coq <
(ifdec (eq_name_dec x m) v (mem m s'))
Coq <
(mem m s')).
Se muestran ahora las propiedades de base de esta función:
Coq < Theorem update_eq : v=(update x s).
Coq < Theorem update_diff_name :
Coq <
(m:name)(s':sort)(~x=m)->(mem m s')=(update m s').
Coq < Theorem update_diff_sort :
Coq <
(m:name)(s':sort)(~s=s')->(mem m s')=(update m s').
Después de tener probados los lemas, la sección puede cerrarse. Las construcciones realizadas se exportan parametrizadas por x,v y m.
Coq < End Update.
Coq < Inductive semcom : memory->com->memory->Prop
Coq < := semskip
: (m:memory)(semcom m skip m)
Coq < | semaff
: (m:memory)(n:name)(s:sort)(v:semval)(e:expr)
Coq <
(exprval m e v)->(semcom m (aff n e) (update n v m))
Coq < | semseq
: (m,m1,m':memory)(c,d:com)(semcom m c m1)->(semcom m1 d m')
Coq <
->(semcom m (seq c d) m')
Coq < | semcondtr : (m,m':memory)(e:expr)(c,d:com)(exprval
m e (Bool true))
Coq <
->(semcom m c m')->(semcom m (cond e c d) m')
Coq < | semcondfa : (m,m':memory)(e:expr)(c,d:com)(exprval
m e (Bool false))
Coq <
->(semcom m d m')->(semcom m (cond e c d) m')
Coq < | semwhiletr : (m,m':memory)(e:expr)(c:com)(exprval
m e (Bool true))
Coq <
->(m1:memory)(semcom m c m1)
Coq <
->(semcom m1 (while e c) m')
Coq <
->(semcom m (while e c) m')
Coq < | semwhilefa : (m:memory)(e:expr)(c:com)(exprval
m e (Bool false))
Coq <
->(semcom m (while e c) m).
Coq < Definition Assertion := memory->Prop.
Coq < Definition Istrue : expr->Assertion
Coq <
:= [E:expr][m:memory](exprval m E (Bool true)).
Coq < Definition Isfalse : expr->Assertion
Coq <
:= [E:expr][m:memory](exprval m E (Bool false)).
Coq < Inductive AndAss [P,Q:Assertion;m:memory]
: Prop :=
Coq <
Conjass : (P m)->(Q m)->(AndAss P Q m).
Coq < Definition ImplAss : Assertion->Assertion->Prop
Coq < := [P,Q:Assertion](m:memory)(P
m)->(Q m).
El transformador siguiente corresponde a lo que sa ha denotado P[X¬ E]. El término (memupdate x E P) representa el predicado que es verdadero en m si P(m[X¬ v]) se satisface, siendo v el valor de la expresión E en la memoria m.
Coq < Definition memupdate : name->expr->Assertion->Assertion
:=
Coq < [x:name][e:expr][P:Assertion][m:memory]
Coq < (v:semval)(exprval m e
v)->(P (update x v m)).
Coq < Inductive trueform : Assertion->com->Assertion->Prop
Coq < := trueskip
: (P:Assertion)(trueform P skip P)
Coq < | trueaff
: (P:Assertion)(n:name)(e:expr)
Coq <
(trueform (memupdate n e P) (aff n e) P)
Coq < | trueseq
: (P,Q,R:Assertion)(c,d:com)
Coq <
(trueform P c R)->(trueform R d Q)
Coq <
->(trueform P (seq c d) Q)
Coq < | truecond
: (P,Q:Assertion)(e:expr)(c,d:com)
Coq <
(trueform (AndAss P (Istrue e)) c Q)
Coq <
->(trueform (AndAss P (Isfalse e)) d Q)
Coq <
->(trueform P (cond e c d) Q)
Coq < | truewhile
: (P:Assertion)(e:expr)(c:com)
Coq <
(trueform (AndAss P (Istrue e)) c P)
Coq <
->(trueform P (while e c) (AndAss P (Isfalse e)))
Coq < | truecons
: (P,P1,Q,Q1:Assertion)(c:com)
Coq <
(ImplAss P P1)->(trueform P1 c Q1)->(ImplAss Q1 Q)
Coq <
->(trueform P c Q).
Coq < Theorem truecorrect :
Coq < (P,Q:Assertion)(c:com)
Coq < (trueform P c Q)->(m1,m2:memory)(semcom
m1 c m2)->(P m1)->(Q m2).
Les han seguido otras pruebas de compiladores: Delphine Terrasse realizó la prueba de un compilador de Esterel, Pablo Argon ha extraido el núcleo del compilador del lenguaje Electre expresado como la ejecución de las reglas de la semántica. Equipos de investigación de Dassault et Aérospatiale estudian actualmente la formalización de un compilador para el lenguaje Lustre implantado en la herramienta Scade.
La formalización de la lógica de Hoare ha sido efectuada por Thomas Kleymann-Schreiber [20, 14] en el asistente de prueba LEGO cuyo lenguaje se parece al del CCI aquí presentado.