KIV-Beweise in Scala 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 253 / 300
Scala und KIV Im zweiten Scala-Versuch wollen wir Beweise programmieren. Zunächst einen Aussagenlogik-Beweiser, dann einen für partielle Korrektheit. Für die Programmierung brauchen wir Teile des aktuellen KIV-Codes. Der KIV-Code ist recht gross (300KLoC) und hat sicher noch Bugs. Ihr müsst davon aber nur wenige Teile kennen. 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 254 / 300
Wiederholung: Syntax von Ausdrücken Ausdrücke e EXPR s (Σ, X ) der Sorte s S über der Signatur Σ und über den Variablen X sind rekursiv definiert durch x aus X s ist ein Ausdruck der Sorte s Sind e 1,..., e n Ausdrücke der Sorten s 1,..., s n und op : s 1,..., s n s, dann ist op(e 1,..., e n ) ein Ausdruck der Sorte s Bem.: Schreibe c statt c() für Konstanten (n = 0) Sind e 1, e 2 Ausdrücke der Sorte s, so ist e 1 = e 2 eine Formel (i. e. ein Ausdruck der Sorte bool) Ist ϕ eine Formel und x eine Variable, so sind x.ϕ und x.ϕ Formeln EXPR(Σ, X ) := s S EXPR s(σ, X ), For(Σ, X ) := EXPR bool (Σ, X ) Terme t T (Σ, X ) sind Ausdrücke ohne Quantoren und Gleichheit. 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 255 / 300
Scala und KIV: Ausdrücke Expressions sind ein freier Datentyp (hier leicht vereinfacht): package types sealed abstract class Expr // Operation case class Op(val name:string, val typ:type) extends Expr // Variable case class Xov(val name:string, val sort:type) extends Expr // Applikation case class Ap(val fct:expr, val termlist:list[expr]) extends Expr //Allquantor case class All(val vl:list[xov], val fma:expr) extends Expr //Existenzquantor case class Ex(val vl:list[xov], val fma:expr) extends Expr... 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 256 / 300
Scala und KIV: Parsen Die rohen Konstuktoren All, Ap etc. machen keinen Typcheck Konstruktoren mit Typcheck sind mkap, mkall etc. Formeleingabe als z.b. mkall(list(x),op( true,... )) möglich In der Praxis nicht gemacht. Stattdessen Aufruf des Parsers Eingabe wie in KIV parser.scalaparse.parse expr( x. true ) (wie in KIV) parse seq(arg:string):seq (Sequenzen), parse spec(arg:string):spec (Spezifikationen) Um Ausdrücke und Sequenzen korrekt zu parsen (Mixfix-Notation!), muss KIV eine Spezifikation kennen : Das geht mit setcurrentspecsig(sp:spec):unit setcurrentspeclistsig(spl:list[spec]):unit 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 257 / 300
Scala und KIV: Pretty-Printing Die rohe Ausgabe von Formeln ist nur für Debugging relevant Normalerweise Ausgabe mit pretty-printer: printer.prettyprint.xpp(arg:any):string mit Sonderzeichen printer.prettyprint.pp(arg:any):string ASCII-Ausgabe Funktioniert für beliebiges Argument arg:spec, arg:expr, etc. Für Beweisbäume (gleich) ist das Ergebnis nur ** A TREE **. Es wird zusätzlich ein Baumfenster (wie in KIV) geöffnet. 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 258 / 300
Scala und KIV: Sequenzen und Bäume Sequenzen und Beweisbäume sind ebenfalls ein Datentyp: package types sealed abstract class Tree case class Seq(ant:List[Expr], suc:list[expr]) extends Tree case class Vtree(concl:Seq, subtr:list[tree]) extends Tree Regelbäume lassen sich graphisch (wie in KIV) anzeigen: Entweder per pretty-printer oder direkt mit printtree.painttree.pp painttree(t:tree):unit. 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 259 / 300
Scala und KIV: Aussagenlogische Regeln Es ist in KIV nicht erlaubt, beliebig Bäume zusammenzubauen Stattdessen: Es gibt vordefinierte Regelbäume für Aussagenlogik: system.basicrules.con r ist der Regelbaum für conjunction right Analog: dis r, equiv l, neg l, imp r, ax etc. Allerdings: Passen nur auf die erste Formel rechts/links (der Antezedent ist eine Liste von Formeln, keine Menge) Deshalb zusätzlich: leftrotate left, leftrotate right bzw. righttrotate left, righttrotate right zum Rotieren im ant bzw. suc. 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 260 / 300
Scala und KIV: Aufbau von Bäumen Beispielregel: $Γ $ϕ, $ $Γ $ψ, $ $Γ $ϕ $ψ, $ con r Regelbäume enthalten Metavariablen wie $ϕ, $ψ und $Γ, $ als Platzhalter für Formeln bzw. Formellisten. Metavariablen kommen in normalen Beweisen nicht vor. 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 261 / 300
Scala und KIV: Aufbau von Bäumen Gegeben ein bestehender Baum (am Anfang: eine Sequenz) so lassen sich Instanzen von Regeln anbauen. refine(tree:tree,i:int,rule:tree):tree wendet die Regel rule auf die i-te Prämisse P (1-basiert) des Baums tree an. Bei der Anwendung werden in ganz R die Metavariablen so durch die konkreten Formeln aus der Regel instanziert (ergibt I(rule)), so dass die Konklusion C der Regel zu P = I(C) wird. rule C P I(rule) P tree tree G G 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 262 / 300
Scala und KIV: Failures (1) refine wirft Error, falls der Baum nicht genug Prämissen hat, Test mit i > tree.premno. generated.treefuns.prem(tree,i) und generated.treefuns.prems(tree) ergeben die i-te Prämisse bzw. alle Prämissen eines tree (1-basiert) des Baums tree Eine Regel kann nicht anwendbar sein die Regelanwendung schlägt fehl = refine wirft die Standardexception Failure. Für con r. Wenn Sukzedent leer, oder erste Formel keine Konjunktion, schlägt refine fehl. 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 263 / 300
Scala und KIV: Failures (2) Fehlschläge sind ein Prinzip beim Beweisen: Regeln, Heuristiken, Simplifierregeln können alle fehlschlagen. spezielle Unterstützung: orl(scalaexpr1,scalaexpr2) wertet zunächst scalaexpr1 aus (z.b. ein refine). Falls das nicht fehlschlägt ist das auch das Ergebnis. Nur falls es fehlschlägt, wird auch scalaexpr2 ausgewertet und liefert das Ergebnis Gewolltes Fehlschlagen durch Aufruf von fail: (i.e. def fail = throw Failure) Bem: orl ist in scala als lazy Funktion realisiert. Erspart es überall zu Schreiben: try {scalaexpr1} catch {x:failure => scalaexpr2} 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 264 / 300
Scala und KIV: Generisches Matching Zu refine gibt es die Verallgmeinerung: mvmatch[s,t](pattern:t, concrete:t, toinst:s):s Stellt fest, ob die Metavariablen in pattern so instanziert werden können, dass concrete herauskommt. (ob pattern auf concrete matcht ) Falls das nicht fehlschlägt, werden die Metavariablen in toinst instanziert, und die Instanz ist das Ergebnis. Variante: mvmatchp(pattern:t, concrete:t):boolean macht einen Test, ob pattern auf concrete matcht. Nützliche Anwendung im Praktikumsversuch: val whilepat = parse seq( $Γ [while $ε do $α]$ϕ ) mvmatch(whilepat, seq, parse prog( $α)) 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 265 / 300
Programmierung eines VCG Für s Praktikum wollen wir einen VCG ( Verification Condition Generator ) für partielle Korrektheit programmieren Ein VCG reduziert Korrektheitsaussagen für Programme auf prädikatenlogische Goals (automatisch, wenn Invarianten gegeben sind). Es zeigt sich: Die Programmierung ist analog zum Aussagenlogischen Beweiser 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 266 / 300
Regeln für Hoare-Kalkül Die DL-Regeln für den Hoare-Kalkül sind analog zu den aussagenlogischen Regeln implementiert Sie enthalten Metavariablen für Programme $α,$β 2 Beispiele (vollständige Listen in der Praktikumsdoku): $Γ $ϕ $Γ [skip]$ϕ skip rule $Γ, $ε [$β] $ϕ $Γ, $ε [$α]$ϕ $Γ [if $ε then $α else $β]$ϕ if forward rule Es gibt kein Normalisieren wie in KIV: Die Regel compound rule muss selbst angewandt werden 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 267 / 300
Spezielle Regeln: besondere Regeln für den Versuch: while rule(inv:expr):tree liefert die while-regel mit eingefüllter konkreter Invariante INV $Γ Inv Inv, $ε [$α]inv Inv, $ε $ϕ $Γ [while $ε do $α]$ϕ (while rule Inv) prakt assign tactic(tree:tree,i:int):tree wendet die Zuweisungsregel mit passender Substitution auf die i-te Prämisse an (x0 neue Variable). x0 = $τ, $Γ ($ϕ) x0 $x $Γ [$x := $τ]$ϕ prakt assign tactic) 25. Juni 2013 G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering 268 / 300