From e8b82dbf9e9693da790dfd70ef20077282e3ee23 Mon Sep 17 00:00:00 2001 From: Frederic Peschanski Date: Tue, 17 Sep 2013 11:33:53 +0200 Subject: [PATCH 01/42] start of parser for mu formulae --- VERSION | 2 +- src/Formula.ml | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Parser.mly | 3 +++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/Formula.ml diff --git a/VERSION b/VERSION index 42e5649..2152110 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v.01-10-2012 +v.16-09-2013 diff --git a/src/Formula.ml b/src/Formula.ml new file mode 100644 index 0000000..333ed5b --- /dev/null +++ b/src/Formula.ml @@ -0,0 +1,65 @@ +(*** Representation of mu-calculus formulae ***) + +open Printf + +open Syntax +open Utils + +(* mu-calculus formulae *) + +type modality = + | FPossibly of prefix list + | FOutPossibly + | FInPossibly + | FAnyPossibly + | FWPossibly of prefix list + | FWOutPossibly + | FWInPossibly + | FWAnyPossibly + | FNecessity of prefix list + | FOutNecessity + | FInNecessity + | FAnyNecessity + | FWNecessity of prefix list + | FWOutNecessity + | FWInNecessity + | FWAnyNecessity + +let string_of_modality : modality -> string = function + | FPossibly(acts) -> string_of_collection "<" ">" "," string_of_prefix acts + | FOutPossibly -> "" + | FInPossibly -> "" + | FAnyPossibly -> "<.>" + | FWPossibly(acts) -> string_of_collection "<<" ">>" "," string_of_prefix acts + | FWOutPossibly -> "<>" + | FWInPossibly -> "<>" + | FWAnyPossibly -> "<<.>>" + | FNecessity(acts) -> string_of_collection "[" "]" "," string_of_prefix acts + | FOutNecessity -> "[!]" + | FInNecessity -> "[?]" + | FAnyNecessity -> "[.]" + | FWNecessity(acts) -> string_of_collection "[[" "]]" "," string_of_prefix acts + | FWOutNecessity -> "[[!]]" + | FWInNecessity -> "[[?]]" + | FWAnyNecessity -> "[[.]]" + +type formula = + | FTrue + | FFalse + | FAnd of formula * formula + | FOr of formula * formula + | FImplies of formula * formula + | FModal of modality * formula + | FInvModal of modality * formula + | FProp of string * (string list) + +let rec string_of_formula : formula -> string = function + | FTrue -> "True" + | FFalse -> "False" + | FAnd(f,g) -> sprintf "(%s and %s)" (string_of_formula f) (string_of_formula g) + | FOr(f,g) -> sprintf "(%s or %s)" (string_of_formula f) (string_of_formula g) + | FImplies(f,g) -> sprintf "(%s ==> %s)" (string_of_formula f) (string_of_formula g) + | FModal(m,f) -> (string_of_modality m) ^ (string_of_formula f) + | FInvModal(m,f) -> "~" ^ (string_of_modality m) ^ (string_of_formula f) + | FProp(prop,params) -> prop ^ (string_of_collection "(" ")" "," (fun s -> s) params) + diff --git a/src/Parser.mly b/src/Parser.mly index 946df37..0eb1ecb 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -334,5 +334,8 @@ | /* empty */ { [] } | expr list_of_exprs { $1::$2 } +/* formula: + | TRUE {*/ + %% (* end of grammar *) From a7434c24b8d22c073c850f4cc9f19e3703ef62e2 Mon Sep 17 00:00:00 2001 From: Frederic Peschanski Date: Tue, 17 Sep 2013 11:57:37 +0200 Subject: [PATCH 02/42] parser for fomula --- src/Formula.ml | 18 +++++++++--------- src/Lexer.mll | 7 +++++++ src/Parser.mly | 45 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/Formula.ml b/src/Formula.ml index 333ed5b..cf6d575 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -2,43 +2,43 @@ open Printf -open Syntax +open Presyntax open Utils (* mu-calculus formulae *) type modality = - | FPossibly of prefix list + | FPossibly of preprefix list | FOutPossibly | FInPossibly | FAnyPossibly - | FWPossibly of prefix list + | FWPossibly of preprefix list | FWOutPossibly | FWInPossibly | FWAnyPossibly - | FNecessity of prefix list + | FNecessity of preprefix list | FOutNecessity | FInNecessity | FAnyNecessity - | FWNecessity of prefix list + | FWNecessity of preprefix list | FWOutNecessity | FWInNecessity | FWAnyNecessity let string_of_modality : modality -> string = function - | FPossibly(acts) -> string_of_collection "<" ">" "," string_of_prefix acts + | FPossibly(acts) -> string_of_collection "<" ">" "," string_of_preprefix acts | FOutPossibly -> "" | FInPossibly -> "" | FAnyPossibly -> "<.>" - | FWPossibly(acts) -> string_of_collection "<<" ">>" "," string_of_prefix acts + | FWPossibly(acts) -> string_of_collection "<<" ">>" "," string_of_preprefix acts | FWOutPossibly -> "<>" | FWInPossibly -> "<>" | FWAnyPossibly -> "<<.>>" - | FNecessity(acts) -> string_of_collection "[" "]" "," string_of_prefix acts + | FNecessity(acts) -> string_of_collection "[" "]" "," string_of_preprefix acts | FOutNecessity -> "[!]" | FInNecessity -> "[?]" | FAnyNecessity -> "[.]" - | FWNecessity(acts) -> string_of_collection "[[" "]]" "," string_of_prefix acts + | FWNecessity(acts) -> string_of_collection "[[" "]]" "," string_of_preprefix acts | FWOutNecessity -> "[[!]]" | FWInNecessity -> "[[?]]" | FWAnyNecessity -> "[[.]]" diff --git a/src/Lexer.mll b/src/Lexer.mll index b39dfbc..dd45b6a 100644 --- a/src/Lexer.mll +++ b/src/Lexer.mll @@ -63,6 +63,10 @@ let tild = "~" let semicol = ";" let ws = (['\t' ' ']*) let colon = ':' + +let implies_1 = "==>" +let implies_2 = "=>" + let cmd_help = "help" let cmd_quit = "quit" let cmd_norm = "norm" @@ -151,6 +155,9 @@ let cmd_wfbisim = "wfbisim" | cmd_bound { BOUND } | cmd_names { NAMES } + | implies_1 { IMPLIES } + | implies_2 { IMPLIES } + | cmd_wderiv { WDERIV } | cmd_tderiv { TDERIV } | cmd_wbisim { WBISIM } diff --git a/src/Parser.mly b/src/Parser.mly index 0eb1ecb..54cda4b 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -3,6 +3,7 @@ open Utils open Presyntax + open Formula let rec mkRes ns p = match ns with @@ -70,11 +71,12 @@ %token IF THEN ELSE INF SUP INFEQ SUPEQ DIFF DOTDOT LACCOL RACCOL /* operators */ -%token PAR PLUS DOT OUT IN MINUS DIV MULT MOD AND OR NOT +%token PAR PLUS DOT OUT IN MINUS DIV MULT MOD AND OR NOT IMPLIES %nonassoc RENAME %left PAR %left AND , OR +%right IMPLIES %nonassoc INF , INFEQ, SUP, SUPEQ, DIFF, EQUAL %left PLUS , MINUS %left MULT , DIV , MOD @@ -93,6 +95,8 @@ %type process %type prefix %type expr +%type modality +%type formula /* grammar */ %% @@ -281,8 +285,12 @@ | expr OUT LPAREN expr RPAREN { PSend($1,$4) } | expr IN LPAREN VAR COLON IDENT RPAREN { PReceive($1,$4,$6) } + list_of_prefixes: + | prefix { [$1] } + | prefix COMMA list_of_prefixes { $1::$3 } + rename : - | LBRACKET list_of_renames RBRACKET { $2 } + | LBRACKET list_of_renames RBRACKET { $2 } list_of_renames : | IDENT DIV IDENT { [($3,$1)] } @@ -334,8 +342,37 @@ | /* empty */ { [] } | expr list_of_exprs { $1::$2 } -/* formula: - | TRUE {*/ + formula: + | TRUE { FTrue } + | FALSE { FFalse } + | formula AND formula { FAnd ($1,$3) } + | formula OR formula { FOr ($1,$3) } + | formula IMPLIES formula { FImplies ($1,$3) } + | modality formula { FModal($1,$2) } + | TILD modality formula { FInvModal($2,$3) } + | IDENT LPAREN list_of_names RPAREN { FProp($1,$3) } + + modality: + | INF list_of_prefixes SUP { FPossibly $2 } + | INF OUT SUP { FOutPossibly } + | INF IN SUP { FInPossibly } + | INF DOT SUP { FAnyPossibly } + + | INF INF list_of_prefixes SUP SUP { FWPossibly $3 } + | INF INF OUT SUP SUP { FWOutPossibly } + | INF INF IN SUP SUP { FWInPossibly } + | INF INF DOT SUP SUP { FWAnyPossibly } + + | LBRACKET list_of_prefixes RBRACKET { FNecessity $2 } + | LBRACKET OUT RBRACKET { FOutNecessity } + | LBRACKET IN RBRACKET { FInNecessity } + | LBRACKET DOT RBRACKET { FAnyNecessity } + + | LBRACKET LBRACKET list_of_prefixes RBRACKET RBRACKET { FWNecessity $3 } + | LBRACKET LBRACKET OUT RBRACKET RBRACKET { FWOutNecessity } + | LBRACKET LBRACKET IN RBRACKET RBRACKET { FWInNecessity } + | LBRACKET LBRACKET DOT RBRACKET RBRACKET { FWAnyNecessity } + %% (* end of grammar *) From 3e957bc6b041d7fe5e47a0197e736be4a3c11db8 Mon Sep 17 00:00:00 2001 From: Frederic Peschanski Date: Tue, 17 Sep 2013 13:31:05 +0200 Subject: [PATCH 03/42] model checking commands (parsing only) --- src/Control.ml | 7 +++++++ src/Formula.ml | 10 ++++++++++ src/Lexer.mll | 33 ++++++++++++++++++++++++++++++++- src/Parser.mly | 20 +++++++++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 9616126..405bd6a 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -301,6 +301,13 @@ let handle_tderiv p = common_deriv (weak_derivatives true) printPfixMap "tderiv" +let handle_prop _ _ _ = failwith "TODO" + +let handle_check_local _ _ = failwith "TODO" + +let handle_check_global _ _ = failwith "TODO" + + diff --git a/src/Formula.ml b/src/Formula.ml index cf6d575..3fddb71 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -52,6 +52,9 @@ type formula = | FModal of modality * formula | FInvModal of modality * formula | FProp of string * (string list) + | FVar of string + | FMu of string * formula + | FNu of string * formula let rec string_of_formula : formula -> string = function | FTrue -> "True" @@ -62,4 +65,11 @@ let rec string_of_formula : formula -> string = function | FModal(m,f) -> (string_of_modality m) ^ (string_of_formula f) | FInvModal(m,f) -> "~" ^ (string_of_modality m) ^ (string_of_formula f) | FProp(prop,params) -> prop ^ (string_of_collection "(" ")" "," (fun s -> s) params) + | FVar(var) -> var + | FMu(x,f) -> sprintf "Mu(%s).%s" x (string_of_formula f) + | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) + + +let formula_of_preformula : formula -> formula = + failwith "TODO" diff --git a/src/Lexer.mll b/src/Lexer.mll index dd45b6a..7decd26 100644 --- a/src/Lexer.mll +++ b/src/Lexer.mll @@ -87,6 +87,21 @@ let cmd_wlts = "wlts" let cmd_wmini = "wmini" let cmd_wfbisim = "wfbisim" +let cmd_prop = "prop" +let cmd_check = "check" +let cmd_check_local = "checklocal" +let cmd_check_global = "checkglobal" + +let sat_1 = "|-" +let sat_2 = "satisfies" + +let mu_1 = "Mu" +let mu_2 = "mu" +let mu_3 = "MU" +let nu_1 = "Nu" +let nu_2 = "nu" +let nu_3 = "NU" + rule token = parse | ws {token lexbuf} @@ -164,7 +179,23 @@ let cmd_wfbisim = "wfbisim" | cmd_wlts { WLTS } | cmd_wmini { WMINI } | cmd_wfbisim { WFBISIM } - + + | mu_1 { MU } + | mu_2 { MU } + | mu_3 { MU } + + | nu_1 { NU } + | nu_2 { NU } + | nu_3 { NU } + + | cmd_prop { PROP } + | cmd_check { CHECK_LOCAL } + | cmd_check_local { CHECK_LOCAL } + | cmd_check_global { CHECK_GLOBAL } + + | sat_1 { SATISFY } + | sat_2 { SATISFY } + | cmd_help { HELP } | cmd_quit { QUIT } | ident as id diff --git a/src/Parser.mly b/src/Parser.mly index 54cda4b..94e1a5d 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -34,7 +34,7 @@ %} /* reserved keywords */ -%token DEF TRUE FALSE END NEW TAU DIV WHEN CONSTDEF TYPEDEF +%token DEF TRUE FALSE END NEW TAU DIV WHEN CONSTDEF TYPEDEF MU NU /* identifiers */ %token IDENT @@ -60,6 +60,11 @@ %token WMINI %token WFBISIM +%token PROP +%token CHECK_LOCAL +%token CHECK_GLOBAL +%token SATISFY + %token HELP %token QUIT @@ -245,6 +250,16 @@ { Control.handle_names (process_of_preprocess $2) } | NAMES error { raise (Fatal_Parse_Error "missing process for names") } + + | PROP IDENT LPAREN list_of_names RPAREN EQUAL formula + { Control.handle_prop $2 $4 (formula_of_preformula $7) } + + | CHECK_LOCAL formula SATISFY process + { Control.handle_check_local (formula_of_preformula $2) (process_of_preprocess $4) } + + | CHECK_GLOBAL formula SATISFY process + { Control.handle_check_global (formula_of_preformula $2) (process_of_preprocess $4) } + | HELP { Control.handle_help () } | QUIT @@ -350,7 +365,10 @@ | formula IMPLIES formula { FImplies ($1,$3) } | modality formula { FModal($1,$2) } | TILD modality formula { FInvModal($2,$3) } + | MU LPAREN IDENT RPAREN DOT formula { FMu ($3,$6) } + | NU LPAREN IDENT RPAREN DOT formula { FNu ($3,$6) } | IDENT LPAREN list_of_names RPAREN { FProp($1,$3) } + | IDENT { FVar($1) } modality: | INF list_of_prefixes SUP { FPossibly $2 } From d2bd43de361c6824cd6cc99e77b3a0bf445a8b9a Mon Sep 17 00:00:00 2001 From: Frederic Peschanski Date: Tue, 1 Oct 2013 13:37:41 +0200 Subject: [PATCH 04/42] modified script --- VERSION | 1 - make-archive.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/VERSION b/VERSION index c786574..94d4d01 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1 @@ v.01-10-2013 - diff --git a/make-archive.sh b/make-archive.sh index fb0b257..3f71db5 100644 --- a/make-archive.sh +++ b/make-archive.sh @@ -2,7 +2,7 @@ TOPFILES="LICENSE.txt AUTHORS VERSION Makefile" -SRCFILES="src/Makefile src/Utils.ml src/Lexer.mll src/Parser.mly src/Syntax.ml src/Normalize.ml src/Minim.ml src/Semop.ml src/Control.ml src/Presyntax.ml src/Pave.ml src/NormTests.ml src/STests.ml src/examples/*.ccs" +SRCFILES="src/Makefile src/Utils.ml src/Lexer.mll src/Parser.mly src/Syntax.ml src/Normalize.ml src/Minim.ml src/Semop.ml src/Control.ml src/Presyntax.ml src/Formula.ml src/Pave.ml src/NormTests.ml src/STests.ml src/examples/*.ccs" DATE=$(date "+%d-%m-%Y") echo "v.${DATE}" > ./VERSION From 619762411836c50cc7e4b4a9f6a01dc1b768848c Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Thu, 10 Oct 2013 16:10:21 +0200 Subject: [PATCH 05/42] rendu compilable --- src/.gitignore | 2 +- src/Formula.ml | 4 ++-- src/Makefile | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/.gitignore b/src/.gitignore index 66679b0..52184ee 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,4 +2,4 @@ _build pave stests lts.dot -lts_mini.dot \ No newline at end of file +lts_mini.dot diff --git a/src/Formula.ml b/src/Formula.ml index 3fddb71..a1067e7 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -70,6 +70,6 @@ let rec string_of_formula : formula -> string = function | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) -let formula_of_preformula : formula -> formula = - failwith "TODO" +let formula_of_preformula (f : formula) : formula = + failwith "TODO : formula_of_preformula" diff --git a/src/Makefile b/src/Makefile index cecc3cb..02ed38d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ SRCS = $(wildcard *.ml *.mli *.mll *.mly) TARGS = stests pave KIND = native # d.byte -FLAGS = -w,Ae,-warn-error,A +FLAGS = -w,Ae#,-warn-error,A all: $(TARGS) From 0752d12114c3774becd073cc1c74f7db818fd449 Mon Sep 17 00:00:00 2001 From: remy Date: Thu, 10 Oct 2013 17:10:35 +0200 Subject: [PATCH 06/42] ~ emacs files + trailing wspaces --- src/.gitignore | 3 ++- src/Formula.ml | 8 ++++---- src/Utils.ml | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/.gitignore b/src/.gitignore index 66679b0..fcb1401 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,4 +2,5 @@ _build pave stests lts.dot -lts_mini.dot \ No newline at end of file +lts_mini.dot +*~ \ No newline at end of file diff --git a/src/Formula.ml b/src/Formula.ml index 3fddb71..c4466d1 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -43,7 +43,7 @@ let string_of_modality : modality -> string = function | FWInNecessity -> "[[?]]" | FWAnyNecessity -> "[[.]]" -type formula = +type formula = | FTrue | FFalse | FAnd of formula * formula @@ -55,7 +55,7 @@ type formula = | FVar of string | FMu of string * formula | FNu of string * formula - + let rec string_of_formula : formula -> string = function | FTrue -> "True" | FFalse -> "False" @@ -68,8 +68,8 @@ let rec string_of_formula : formula -> string = function | FVar(var) -> var | FMu(x,f) -> sprintf "Mu(%s).%s" x (string_of_formula f) | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) - + let formula_of_preformula : formula -> formula = - failwith "TODO" + failwith "TODO" diff --git a/src/Utils.ml b/src/Utils.ml index fd5cd6b..6b14ee0 100644 --- a/src/Utils.ml +++ b/src/Utils.ml @@ -29,7 +29,7 @@ let string_of_args tostr lst = string_of_collection "(" ")" "," tostr lst let string_of_set tostr set = string_of_collection "{" "}" "," tostr (SSet.elements set) -let string_of_map map = +let string_of_map map = string_of_collection "{" "}" "," (fun (old,value) -> sprintf "%s/%s" value old) (SMap.bindings map) @@ -51,7 +51,7 @@ let forget _ = () (* permutations:: 'a list -> 'a list list *) let rec permutations = - let rec inject_all e n l llen = + let rec inject_all e n l llen = if n > llen then [] else (inject e n l)::(inject_all e (n+1) l llen) and inject e n l = From 3f56914fd8cdee44af01f156cc442643e386e739 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Thu, 10 Oct 2013 17:14:25 +0200 Subject: [PATCH 07/42] trailing --- src/Formula.ml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Formula.ml b/src/Formula.ml index 3a7f5cc..5fbf4e1 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -70,11 +70,6 @@ let rec string_of_formula : formula -> string = function | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) -<<<<<<< HEAD -let formula_of_preformula : formula -> formula = - failwith "TODO" -======= -let formula_of_preformula (f : formula) : formula = - failwith "TODO : formula_of_preformula" ->>>>>>> 619762411836c50cc7e4b4a9f6a01dc1b768848c +let formula_of_preformula (f : formula) : formula = + failwith "TODO : formula_of_preformula" From 98160c720381736b5842d99bd5aaf065cc8f641a Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Thu, 10 Oct 2013 17:50:00 +0200 Subject: [PATCH 08/42] Ajout de messages d'erreur lisibles en interactif --- src/Pave.ml | 13 +++++++++++-- src/Presyntax.ml | 14 ++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Pave.ml b/src/Pave.ml index 8d0583e..11117c8 100644 --- a/src/Pave.ml +++ b/src/Pave.ml @@ -36,12 +36,13 @@ Arg.parse [ printf "%s\n%!" banner;; -let parse_error_msg lexbuf = +let parse_error_msg ?interactive_mode:(inter=false) lexbuf = let p = lexbuf.Lexing.lex_curr_p in let l = p.Lexing.pos_lnum in let c = p.Lexing.pos_cnum - p.Lexing.pos_bol in let tok = Lexing.lexeme lexbuf in + if inter then printf "%s^\n" @@ String.make (c + 1) ' '; printf "Parser error at line %d char %d: ~%s~\n%!" l c tok ;; match !load_file with @@ -59,7 +60,14 @@ match !load_file with parse_error_msg lexbuf ; printf " ==> %s\n%!" msg | Parsing.Parse_error -> - parse_error_msg lexbuf + parse_error_msg ~interactive_mode:true lexbuf + + | Presyntax.Type_Exception msg -> + printf " ==> %s\n%!" msg + | Presyntax.Vardef_Exception name -> + printf " ==> Undefined var \"%s\"\n%!" name + | Presyntax.Typedef_Exception name -> + printf " ==> Undefined type \"%s\"\n%!" name done | Some file -> printf "Loading file %s... \n%!" file; @@ -76,6 +84,7 @@ match !load_file with printf " ==> %s\n%!" msg ; true | Parsing.Parse_error -> parse_error_msg lexbuf ; true + in if continue then loop (); in diff --git a/src/Presyntax.ml b/src/Presyntax.ml index f2c6fc3..4fe6cea 100644 --- a/src/Presyntax.ml +++ b/src/Presyntax.ml @@ -5,6 +5,9 @@ open Utils open Syntax +exception Typedef_Exception of string +exception Vardef_Exception of string ;; + let env_const = ref SMap.empty ;; let env_var = ref SMap.empty ;; @@ -81,7 +84,9 @@ let rec interprete_preexpr : preexpr -> value = function | PInt i -> Int i | PName str -> Name str | PConst name -> Int (SMap.find name !env_const) - | PVar name -> (SMap.find name !env_var) + | PVar name -> begin + try (SMap.find name !env_var) with Not_found -> raise @@ Vardef_Exception name + end | PNot pexpr -> let b = bool_of_value (interprete_preexpr pexpr) in Bool (not b) | PAnd (preexpr1, preexpr2) -> let b1 = bool_of_value (interprete_preexpr preexpr1) @@ -221,7 +226,6 @@ let rec string_of_preprocess = function | PGuard(g,p) -> sprintf "when (%s) %s" (string_of_preexpr g) (string_of_preprocess p) -exception Vardef_Exception of string ;; let make_int_list min max = let rec make_aux m = @@ -339,8 +343,10 @@ let definitions_of_predefinition : predefinition -> definition list = | (PParamInt i)::tl -> def_of_predef_aux name (computed_params@[Int i]) tl preproc | (PParamVar (nomVar, theType))::tl -> (if SMap.mem nomVar !env_var then - raise (Vardef_Exception nomVar)); - let val_list = value_list (SMap.find theType !env_type) in + raise @@ Vardef_Exception nomVar); + let val_list = + try value_list (SMap.find theType !env_type) with Not_found -> raise @@ Typedef_Exception theType + in let def_list = List.map (function v -> env_var := (SMap.add nomVar v !env_var); (def_of_predef_aux name (computed_params@[v]) tl preproc) ) From c78c1aca490e58e8974f3279f048e6d70b197bef Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Thu, 10 Oct 2013 17:52:34 +0200 Subject: [PATCH 09/42] Changement gitignore --- src/.gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/.gitignore b/src/.gitignore index fcb1401..875cd71 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -3,4 +3,6 @@ pave stests lts.dot lts_mini.dot -*~ \ No newline at end of file +*~ +*.swp +.vimrc From 2b990d52db22ed14fa808fc7a4091a793e4d6644 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Thu, 10 Oct 2013 18:49:12 +0200 Subject: [PATCH 10/42] TODO dans le un readme --- src/Control.ml | 74 +++++++++++++++++++------------------- src/Formula.ml | 14 ++++++-- src/Lexer.mll | 20 +++++------ src/Parser.mly | 93 ++++++++++++++++++++++++------------------------ src/Presyntax.ml | 72 ++++++++++++++++++------------------- src/REAMDE | 26 ++++++++++++++ 6 files changed, 168 insertions(+), 131 deletions(-) create mode 100644 src/REAMDE diff --git a/src/Control.ml b/src/Control.ml index 405bd6a..2a174f9 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -39,11 +39,11 @@ let script_mode = ref false ;; exception Constdef_Exception of string ;; exception Typedef_Exception of string ;; -let handle_help () = +let handle_help () = printf "%s\n> %!" help_me let handle_quit () = - printf "bye bye !\n%!" ; + printf "bye bye !\n%!" ; exit 0 let timing operation = @@ -51,13 +51,13 @@ let timing operation = in let result = operation() in let end_time = Sys.time() in - (result, end_time -. start_time) + (result, end_time -. start_time) let handle_constdef (const_name:string) (const_val:int) = (* printf "(handle_constdef %s %d)\n%!" const_name const_val ; *) if not (SMap.mem const_name !Presyntax.env_const) then Presyntax.env_const := SMap.add const_name const_val !Presyntax.env_const - else + else raise (Constdef_Exception const_name) ;; @@ -65,25 +65,25 @@ let handle_typedef_range (type_name:string) (min_val:string) (max_val:string) = (* printf "(handle_typedef_range %s %s %s)\n%!" type_name min_val max_val ; *) if not (SMap.mem type_name !Presyntax.env_type) then let find_val v = - try - SMap.find v !Presyntax.env_const + try + SMap.find v !Presyntax.env_const with - Not_found -> - try - int_of_string v - with - Failure _ -> raise (Typedef_Exception type_name) + Not_found -> + try + int_of_string v + with + Failure _ -> raise (Typedef_Exception type_name) in let min = find_val min_val and max = find_val max_val in - - Presyntax.add_to_env_type type_name - ( if min < max then - Presyntax.PTDefRange (type_name, min, max) - else - Presyntax.PTDefRange (type_name, max, min) - ) - else + + Presyntax.add_to_env_type type_name + ( if min < max then + Presyntax.PTDefRange (type_name, min, max) + else + Presyntax.PTDefRange (type_name, max, min) + ) + else raise (Typedef_Exception type_name) ;; @@ -96,7 +96,7 @@ let handle_typedef_enum (type_name:string) (names:string list) = in if not (SMap.mem type_name !Presyntax.env_type) then Presyntax.add_to_env_type type_name ( Presyntax.PTDefEnum (type_name, list2set names) ) - else + else raise (Typedef_Exception type_name) ;; @@ -122,7 +122,7 @@ let handle_normalization proc = let proc',time = timing (fun () -> normalize proc) in printf "%s\n%!" (string_of_nprocess proc') ; - printf "(elapsed time=%fs)\n%!" time + printf "(elapsed time=%fs)\n%!" time let handle_struct_congr p q = if !script_mode then @@ -133,7 +133,7 @@ let handle_struct_congr p q = (if ok then printf "the processes *are* structurally congruent\n%!" else printf "the processes are *not* structurally congruent\n%!") ; - printf "(elapsed time=%fs)\n%!" time + printf "(elapsed time=%fs)\n%!" time let global_definition_map = Hashtbl.create 64 @@ -143,12 +143,12 @@ let common_deriv f_deriv f_print str str2 p = printf "Compute %s...\n%!" str2; let op = fun () -> let np = normalize p in - f_deriv global_definition_map np + f_deriv global_definition_map np in let derivs, time = timing op in f_print derivs; - printf "(elapsed time=%fs)\n%!" time + printf "(elapsed time=%fs)\n%!" time let fetch_definition key = Hashtbl.find global_definition_map key @@ -166,7 +166,7 @@ let dot_style_format (p, l, p') = sprintf "\"%s\" -> \"%s\" [ label = \"%s\", fontcolor=red ]" (string_of_nprocess p) (string_of_nprocess p') (string_of_label l) -let dot_style_format' (pl, l, pl') = +let dot_style_format' (pl, l, pl') = sprintf "\"%s\" -> \"%s\" [ label = \"%s\", fontcolor=red ]" (string_of_list string_of_nprocess pl) (string_of_list string_of_nprocess pl') @@ -175,7 +175,7 @@ let dot_style_format' (pl, l, pl') = let common_lts f str p = if !script_mode then printf "> %s %s\n%!" str (string_of_process p) ; - let transs, time = timing (fun () -> f global_definition_map (normalize p)) + let transs, time = timing (fun () -> f global_definition_map (normalize p)) in List.iter (fun t -> printf "%s\n" (string_of_transition t)) transs; printf "\nGenerating %s.dot... %!" str; @@ -194,23 +194,23 @@ let common_lts f str p = fprintf oc "}\n"; close_out oc; printf "done\n(elapsed time=%fs)\n%!" time - + let common_minimization f_deriv str proc = if !script_mode then printf "> %s %s\n%!" str (string_of_process proc) ; printf "Minimize process...\n%!"; let transs, time = timing (fun () -> let p = normalize proc in - minimize f_deriv global_definition_map p) + minimize f_deriv global_definition_map p) in List.iter (fun t -> printf "%s\n" (string_of_transitions t)) transs; printf "\nGenerating lts_mini.dot... %!"; - let nprocs = + let nprocs = List.fold_left (fun acc (x, _, y) -> x::(y::acc)) [] transs in let oc = open_out "lts_mini.dot" in fprintf oc "digraph LTSMINI {\n"; - List.iter + List.iter (fun x -> fprintf oc "\"%s\" [ fontcolor=blue ]\n" (string_of_list string_of_nprocess x)) nprocs; @@ -221,7 +221,7 @@ let common_minimization f_deriv str proc = printf "done\n(elapsed time=%fs)\n%!" time -let common_bisim f_bisim str str2 str3 p1 p2 = +let common_bisim f_bisim str str2 str3 p1 p2 = if !script_mode then printf "> %s %s ~ %s\n%!" str (string_of_process p1) (string_of_process p2) ; printf "Calculate %s...\n%!" str2; @@ -230,7 +230,7 @@ let common_bisim f_bisim str str2 str3 p1 p2 = let np1 = normalize p1 in let np2 = normalize p2 in try - let bsm = f_bisim global_definition_map np1 np2 + let bsm = f_bisim global_definition_map np1 np2 in let end_time = Sys.time() in @@ -252,10 +252,10 @@ let common_is_bisim f_bisim str str2 p1 p2 = let np2 = normalize p2 in f_bisim global_definition_map np1 np2) in - if ok + if ok then printf "the processes *are* %s\n(elapsed time=%fs)\n%!" str2 time else printf "the processes are *not* %s\n(elapsed time=%fs)\n%!" str2 time - + let common_is_fbisim f_deriv str1 str2 p1 p2 = if !script_mode then printf "> %s ? %s ~ %s\n%!" str1 (string_of_process p1) (string_of_process p2) ; @@ -301,11 +301,11 @@ let handle_tderiv p = common_deriv (weak_derivatives true) printPfixMap "tderiv" -let handle_prop _ _ _ = failwith "TODO" +let handle_prop _ _ _ = assert false (* TODO *) -let handle_check_local _ _ = failwith "TODO" +let handle_check_local _ _ = assert false (* TODO *) -let handle_check_global _ _ = failwith "TODO" +let handle_check_global _ _ = assert false (* TODO *) diff --git a/src/Formula.ml b/src/Formula.ml index 5fbf4e1..2cbea2d 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -70,6 +70,16 @@ let rec string_of_formula : formula -> string = function | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) -let formula_of_preformula (f : formula) : formula = - failwith "TODO : formula_of_preformula" +let rec formula_of_preformula : formula -> formula = (* function + | FTrue + | FFalse + | FAnd (f, g) + | FOr (f, g) + | FImplies (f, g) + | FModal (m, f) + | FInvModal (m, f) + | FProp (prop, params) + | FVar var + | FMu (x, f) + | FNu (x, f) -> *) assert false (*TODO*) diff --git a/src/Lexer.mll b/src/Lexer.mll index 7decd26..3eeb43b 100644 --- a/src/Lexer.mll +++ b/src/Lexer.mll @@ -1,5 +1,5 @@ -{ +{ open Parser let line=ref 1 @@ -11,7 +11,7 @@ let digit = ['0'-'9'] let int = (['1'-'9'] digit*) let cmt = ('#' [^'\n']*) - + let r_def = "def" let r_true = "true" let r_false = "false" @@ -105,7 +105,7 @@ let nu_3 = "NU" rule token = parse | ws {token lexbuf} - | eol + | eol { incr line; token lexbuf } | cmt @@ -134,8 +134,8 @@ let nu_3 = "NU" | r_const { CONSTDEF } | r_type { TYPEDEF } | dotdot { DOTDOT } - | op_dot { DOT } - | op_plus { PLUS } + | op_dot { DOT } + | op_plus { PLUS } | op_minus { MINUS } | op_par { PAR } | op_out { OUT } @@ -169,7 +169,7 @@ let nu_3 = "NU" | cmd_free { FREE } | cmd_bound { BOUND } | cmd_names { NAMES } - + | implies_1 { IMPLIES } | implies_2 { IMPLIES } @@ -195,14 +195,14 @@ let nu_3 = "NU" | sat_1 { SATISFY } | sat_2 { SATISFY } - + | cmd_help { HELP } | cmd_quit { QUIT } - | ident as id + | ident as id { IDENT (id) } | eof { EOF } - | _ { failwith((Lexing.lexeme lexbuf) ^ + | _ { failwith((Lexing.lexeme lexbuf) ^ ": mistake at line " ^ string_of_int !line)} - + { } diff --git a/src/Parser.mly b/src/Parser.mly index 94e1a5d..4cd6d18 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -10,12 +10,12 @@ | [] -> p | n::ns' -> PRes(n,(mkRes ns' p)) - let mkRename ns p = + let mkRename ns p = let rec ren = function | [] -> p | (old,value) :: ns' -> PRename(old,value,(ren ns')) in - ren (List.rev ns) + ren (List.rev ns) (* @@ -108,72 +108,72 @@ script: | EOF { false } | statement SEMICOL { true } - | statement error { raise (Fatal_Parse_Error "missing ';' after statement") } + | statement error { raise @@ Fatal_Parse_Error "missing ';' after statement"} minmax: | CONST { $1 } | INT { (string_of_int $1) } statement: - | definition + | definition { let defs = definitions_of_predefinition $1 in - List.iter Control.handle_definition defs + List.iter Control.handle_definition defs } - | CONSTDEF CONST EQUAL INT + | CONSTDEF CONST EQUAL INT { Control.handle_constdef $2 $4 } | TYPEDEF IDENT EQUAL LBRACKET minmax DOTDOT minmax RBRACKET { Control.handle_typedef_range $2 $5 $7 } | TYPEDEF IDENT EQUAL LACCOL list_of_names RACCOL { Control.handle_typedef_enum $2 $5 } - | NORM process + | NORM process { Control.handle_normalization (process_of_preprocess $2) } - | NORM error + | NORM error { raise (Fatal_Parse_Error "missing process to normalize") } | STRUCT process EQEQ process - { Control.handle_struct_congr + { Control.handle_struct_congr (process_of_preprocess $2) (process_of_preprocess $4) } | STRUCT process error { raise (Fatal_Parse_Error "missing '==' for structural congruence") } | STRUCT process EQEQ error { raise (Fatal_Parse_Error "missing process after '==' for structural congruence") } - | STRUCT error + | STRUCT error {raise (Fatal_Parse_Error "missing process before '==' for structural congruence") } - | BISIM IN process TILD process - { Control.handle_is_bisim - (process_of_preprocess $3) + | BISIM IN process TILD process + { Control.handle_is_bisim + (process_of_preprocess $3) (process_of_preprocess $5) } | BISIM IN process error { raise (Fatal_Parse_Error "missing '~' for strong bisimilarity") } | BISIM IN process TILD error { raise (Fatal_Parse_Error "missing process after '~' for strong bisimilarity") } | BISIM process TILD process - { Control.handle_bisim + { Control.handle_bisim (process_of_preprocess $2) (process_of_preprocess $4) } | BISIM process error { raise (Fatal_Parse_Error "missing '~' for strong bisimilarity") } | BISIM process TILD error { raise (Fatal_Parse_Error "missing process after '~' for strong bisimilarity") } - | BISIM error + | BISIM error { raise (Fatal_Parse_Error "missing '?' or process before '~' for strong bisimilarity") } - | FBISIM IN process TILD process - { Control.handle_is_fbisim + | FBISIM IN process TILD process + { Control.handle_is_fbisim (process_of_preprocess $3) (process_of_preprocess $5) } | FBISIM IN process error { raise (Fatal_Parse_Error "missing '~' for strong bisimilarity") } | FBISIM IN process TILD error { raise (Fatal_Parse_Error "missing process after '~' for strong bisimilarity") } - | FBISIM error + | FBISIM error { raise (Fatal_Parse_Error "missing '?' or process before '~' for strong bisimilarity") } - | WBISIM IN process TILD TILD process - { Control.handle_is_wbisim - (process_of_preprocess $3) + | WBISIM IN process TILD TILD process + { Control.handle_is_wbisim + (process_of_preprocess $3) (process_of_preprocess $6) } | WBISIM IN process error { raise (Fatal_Parse_Error "missing '~~' for weak bisimilarity") } @@ -182,9 +182,9 @@ | WBISIM IN process TILD TILD error { raise (Fatal_Parse_Error "missing process after '~~' for weak bisimilarity") } - | WBISIM process TILD TILD process - { Control.handle_wbisim - (process_of_preprocess $2) + | WBISIM process TILD TILD process + { Control.handle_wbisim + (process_of_preprocess $2) (process_of_preprocess $5) } | WBISIM process error { raise (Fatal_Parse_Error "missing '~~' for weak bisimilarity") } @@ -192,13 +192,13 @@ { raise (Fatal_Parse_Error "missing '~' for weak bisimilarity") } | WBISIM process TILD TILD error { raise (Fatal_Parse_Error "missing process after '~~' for weak bisimilarity") } - - | WBISIM error + + | WBISIM error { raise (Fatal_Parse_Error "missing '?' or process before '~~' for weak bisimilarity") } - | WFBISIM IN process TILD TILD process - { Control.handle_is_fwbisim - (process_of_preprocess $3) + | WFBISIM IN process TILD TILD process + { Control.handle_is_fwbisim + (process_of_preprocess $3) (process_of_preprocess $6) } | WFBISIM IN process error { raise (Fatal_Parse_Error "missing '~~' for weak bisimilarity") } @@ -210,48 +210,49 @@ | WDERIV process { Control.handle_wderiv (process_of_preprocess $2) } | WDERIV error - { raise (Fatal_Parse_Error "missing process to derivate") } + { raise (Fatal_Parse_Error "missing process to derivate") } | TDERIV process { Control.handle_tderiv (process_of_preprocess $2) } | TDERIV error - { raise (Fatal_Parse_Error "missing process to derivate") } + { raise (Fatal_Parse_Error "missing process to derivate") } | WLTS process { Control.handle_wlts (process_of_preprocess $2) } | WLTS error - { raise (Fatal_Parse_Error "missing process for LTS") } + { raise (Fatal_Parse_Error "missing process for LTS") } | WMINI process { Control.handle_wminimization (process_of_preprocess $2) } | WMINI error - {raise (Fatal_Parse_Error "missing process for minimization") } - + {raise (Fatal_Parse_Error "missing process for minimization") } + | DERIV process { Control.handle_deriv (process_of_preprocess $2) } | DERIV error - { raise (Fatal_Parse_Error "missing process to derivate") } + { raise (Fatal_Parse_Error "missing process to derivate") } | LTS process { Control.handle_lts (process_of_preprocess $2) } | LTS error - { raise (Fatal_Parse_Error "missing process for LTS") } + { raise (Fatal_Parse_Error "missing process for LTS") } | MINI process { Control.handle_minimization (process_of_preprocess $2) } | MINI error - {raise (Fatal_Parse_Error "missing process for minimization") } + {raise (Fatal_Parse_Error "missing process for minimization") } | FREE process { Control.handle_free (process_of_preprocess $2) } | FREE error - { raise (Fatal_Parse_Error "missing process for free names") } + { raise (Fatal_Parse_Error "missing process for free names") } | BOUND process { Control.handle_bound (process_of_preprocess $2) } | BOUND error - { raise (Fatal_Parse_Error "missing process for bound names") } + { raise (Fatal_Parse_Error "missing process for bound names") } | NAMES process { Control.handle_names (process_of_preprocess $2) } | NAMES error - { raise (Fatal_Parse_Error "missing process for names") } + { raise (Fatal_Parse_Error "missing process for names") } | PROP IDENT LPAREN list_of_names RPAREN EQUAL formula + (* prop_name params fmla *) { Control.handle_prop $2 $4 (formula_of_preformula $7) } | CHECK_LOCAL formula SATISFY process @@ -266,20 +267,20 @@ { Control.handle_quit () } process: - | INT - { if $1 = 0 then PSilent + | INT + { if $1 = 0 then PSilent else raise (Fatal_Parse_Error "Only 0 can be used as Silent process") } - | END + | END { PSilent } | prefix { PPrefix($1,PSilent) } | prefix COMMA process { PPrefix($1,$3) } | prefix COMMA error { raise (Fatal_Parse_Error "right-hand process missing after prefix") } | prefix error - { raise (Fatal_Parse_Error "missing ',' after prefix") } + { raise (Fatal_Parse_Error "missing ',' after prefix") } | process PAR process { PPar($1,$3) } | process PAR error - { raise (Fatal_Parse_Error "right-hand process missing in parallel") } + { raise (Fatal_Parse_Error "right-hand process missing in parallel") } | process PLUS process { PSum($1,$3) } | process PLUS error { raise (Fatal_Parse_Error "right-hand process missing in sum") } @@ -302,7 +303,7 @@ list_of_prefixes: | prefix { [$1] } - | prefix COMMA list_of_prefixes { $1::$3 } + | prefix COMMA list_of_prefixes { $1::$3 } rename : | LBRACKET list_of_renames RBRACKET { $2 } diff --git a/src/Presyntax.ml b/src/Presyntax.ml index f2c6fc3..b73f89f 100644 --- a/src/Presyntax.ml +++ b/src/Presyntax.ml @@ -14,7 +14,7 @@ type preconstdef = let string_of_preconstdef = function | PConstDef (name,value) -> sprintf "const %%%s = %d" name value - + type pretypedef = | PTDefRange of string * int * int | PTDefEnum of string * SSet.t @@ -22,7 +22,7 @@ type pretypedef = -let env_type = +let env_type = let bool_type = PTDefEnum( "Bool", (SSet.add "True" (SSet.add "False" SSet.empty))) in ref (SMap.add "Bool" bool_type SMap.empty) ;; @@ -34,7 +34,7 @@ let add_to_env_type k v = let string_of_pretypedef = function | PTDefRange (name,min,max) -> sprintf "type %s = [%d..%d]" name min max | PTDefEnum (name,names) -> "type " ^ name ^ " = " ^ (string_of_set (fun x -> x) names) - + type preexpr = | PTrue | PFalse @@ -60,7 +60,7 @@ type preexpr = exception Type_Exception of string -let bool_of_value = function +let bool_of_value = function | Bool b -> b | Name n -> raise (Type_Exception (sprintf "Name %s was received where Bool was expected !!!" n)) | Int i -> raise (Type_Exception (sprintf "Int %d was received where Bool was expected !!!" i)) @@ -74,7 +74,7 @@ let int_of_value = function | Bool b -> raise (Type_Exception (sprintf "Bool %s was received where Int was expected !!!" (if b then "true" else "false"))) | Name n -> raise (Type_Exception (sprintf "Name %s was received where Int was expected !!!" n)) | Int i -> i - + let rec interprete_preexpr : preexpr -> value = function | PTrue -> Bool true | PFalse -> Bool false @@ -83,14 +83,14 @@ let rec interprete_preexpr : preexpr -> value = function | PConst name -> Int (SMap.find name !env_const) | PVar name -> (SMap.find name !env_var) | PNot pexpr -> let b = bool_of_value (interprete_preexpr pexpr) in Bool (not b) - | PAnd (preexpr1, preexpr2) -> + | PAnd (preexpr1, preexpr2) -> let b1 = bool_of_value (interprete_preexpr preexpr1) and b2 = bool_of_value (interprete_preexpr preexpr2) in - Bool ( b1 && b2 ) + Bool ( b1 && b2 ) | POr (preexpr1, preexpr2) -> let b1 = bool_of_value (interprete_preexpr preexpr1) and b2 = bool_of_value (interprete_preexpr preexpr2) in - Bool ( b1 || b2 ) + Bool ( b1 || b2 ) | PAdd (preexpr1, preexpr2) -> let i1 = int_of_value (interprete_preexpr preexpr1 ) @@ -135,7 +135,7 @@ let rec interprete_preexpr : preexpr -> value = function | (Int i1, Int i2) -> Bool ( i1 = i2 ) | (Name n1, Name n2) -> Bool ( n1 = n2 ) | (_, _) -> Bool ( false )) - + | PNeq (preexpr1, preexpr2) -> let p1 = interprete_preexpr preexpr1 and p2 = interprete_preexpr preexpr2 in @@ -149,12 +149,12 @@ let rec interprete_preexpr : preexpr -> value = function let i1 = int_of_value (interprete_preexpr preexpr1 ) and i2 = int_of_value ( interprete_preexpr preexpr2 ) in Bool ( i1 <= i2 ) - + | PSupEq (preexpr1, preexpr2) -> let i1 = int_of_value (interprete_preexpr preexpr1 ) and i2 = int_of_value ( interprete_preexpr preexpr2 ) in Bool ( i1 >= i2 ) - + | PIf (cond, preexpr1, preexpr2) -> let b = bool_of_value (interprete_preexpr cond) in if b then @@ -181,7 +181,7 @@ let rec string_of_preexpr = function | PInf (e1,e2) -> sprintf "(%s) < (%s)" (string_of_preexpr e1) (string_of_preexpr e2) | PSup (e1,e2) -> sprintf "(%s) > (%s)" (string_of_preexpr e1) (string_of_preexpr e2) | PEq (e1,e2) -> sprintf "(%s) = (%s)" (string_of_preexpr e1) (string_of_preexpr e2) - | PNeq (e1,e2) -> sprintf "(%s) <> (%s)" (string_of_preexpr e1) (string_of_preexpr e2) + | PNeq (e1,e2) -> sprintf "(%s) <> (%s)" (string_of_preexpr e1) (string_of_preexpr e2) | PInfEq (e1,e2) -> sprintf "(%s) <= (%s)" (string_of_preexpr e1) (string_of_preexpr e2) | PSupEq (e1,e2) -> sprintf "(%s) >= (%s)" (string_of_preexpr e1) (string_of_preexpr e2) | PIf (c,e1,e2) -> sprintf "if (%s) then (%s) else (%s)" (string_of_preexpr c) (string_of_preexpr e1) (string_of_preexpr e2) @@ -236,17 +236,17 @@ let value_list : pretypedef -> (value list) = function | PTDefRange (_, min,max) ->(make_int_list min max) | PTDefEnum ("Bool" , _) -> [Bool true; Bool false] | PTDefEnum (_ , enum) -> (List.map (fun a -> Name a) (SSet.elements enum)) - + let rec process_of_receive : string -> string -> pretypedef -> preprocess -> process = fun canal nomVar theType pproc -> let val_list = value_list theType in let rec process_of_receive_aux v_list= match v_list with - | [] -> failwith "Empty list" + | [] -> failwith "Empty list" | hd::[] -> (env_var := (SMap.add nomVar hd !env_var); Prefix( In( sprintf "%s_%s" canal (string_of_value hd)), (process_of_preprocess pproc) )) - | hd::tl -> + | hd::tl -> env_var:=(SMap.add nomVar hd !env_var); let pref = Prefix( In( sprintf "%s_%s" canal (string_of_value hd)), (process_of_preprocess pproc) ) in @@ -257,37 +257,37 @@ let rec process_of_receive : string -> string -> pretypedef -> preprocess -> pro and process_of_prefix : preprefix -> preprocess -> process = fun pfix pproc -> match pfix with - | PTau -> Prefix(Tau, (process_of_preprocess pproc) ) + | PTau -> Prefix(Tau, (process_of_preprocess pproc) ) | PIn (pexpr) -> Prefix( In ( string_of_value (interprete_preexpr pexpr)), - (process_of_preprocess pproc) ) + (process_of_preprocess pproc) ) | POut(pexpr) -> Prefix( Out ( string_of_value (interprete_preexpr pexpr)), - (process_of_preprocess pproc) ) - - | PSend(pexprCanal, pexprVal) -> Prefix( Out ( sprintf "%s_%s" + (process_of_preprocess pproc) ) + + | PSend(pexprCanal, pexprVal) -> Prefix( Out ( sprintf "%s_%s" (string_of_value (interprete_preexpr pexprCanal)) (string_of_value (interprete_preexpr pexprVal)) ), (process_of_preprocess pproc)) - | PReceive(pexprCanal, nomVar, nomType) -> + | PReceive(pexprCanal, nomVar, nomType) -> (if SMap.mem nomVar !env_var then raise (Vardef_Exception nomVar)); - let canal = string_of_value (interprete_preexpr pexprCanal) + let canal = string_of_value (interprete_preexpr pexprCanal) and theType = SMap.find nomType !env_type in let prc = process_of_receive canal nomVar theType pproc in (env_var := SMap.remove nomVar !env_var; prc) -and process_of_preprocess : preprocess -> process = - fun preproc -> +and process_of_preprocess : preprocess -> process = + fun preproc -> printf "Transforming process:\n%s\n%!" (string_of_preprocess preproc) ; match preproc with | PSilent -> Silent | PPrefix (pfix, pproc) -> process_of_prefix pfix pproc - - | PSum (pproc1, pproc2) -> - Sum( (process_of_preprocess pproc1), + + | PSum (pproc1, pproc2) -> + Sum( (process_of_preprocess pproc1), (process_of_preprocess pproc2) ) - | PPar (pproc1, pproc2) -> - Par( (process_of_preprocess pproc1), + | PPar (pproc1, pproc2) -> + Par( (process_of_preprocess pproc1), (process_of_preprocess pproc2) ) | PRes (nvar, pproc) -> @@ -298,13 +298,13 @@ and process_of_preprocess : preprocess -> process = | PRename( oldName, newName , pproc) -> Rename( oldName, newName, (process_of_preprocess pproc) ) - | PGuard( pexpr, ppro) -> + | PGuard( pexpr, ppro) -> let b = bool_of_value (interprete_preexpr pexpr) in if b then process_of_preprocess ppro else Silent - + type preparam = | PParamVar of string * string | PParamBool of bool @@ -319,7 +319,7 @@ let string_of_preparam = function type predefinition = PDefinition of string * preparam list * preprocess -let string_of_predef_header (PDefinition (name,params,_)) = +let string_of_predef_header (PDefinition (name,params,_)) = name ^ (string_of_args string_of_preparam params) let string_of_predefinition = function @@ -332,16 +332,16 @@ let definitions_of_predefinition : predefinition -> definition list = (* let rec def_of_predef_aux : string -> value list -> preparam list -> preprocess -> definition list= *) (* function name computed_params preparams preproc -> *) let rec def_of_predef_aux name computed_params preparams preproc = - match preparams with + match preparams with | [] -> [ Definition (name, computed_params, process_of_preprocess preproc) ] | (PParamBool b)::tl -> def_of_predef_aux name (computed_params@[Bool b]) tl preproc | (PParamName n)::tl -> def_of_predef_aux name (computed_params@[Name n]) tl preproc | (PParamInt i)::tl -> def_of_predef_aux name (computed_params@[Int i]) tl preproc - | (PParamVar (nomVar, theType))::tl -> + | (PParamVar (nomVar, theType))::tl -> (if SMap.mem nomVar !env_var then raise (Vardef_Exception nomVar)); let val_list = value_list (SMap.find theType !env_type) in - let def_list = List.map (function v -> + let def_list = List.map (function v -> env_var := (SMap.add nomVar v !env_var); (def_of_predef_aux name (computed_params@[v]) tl preproc) ) val_list @@ -352,4 +352,4 @@ let definitions_of_predefinition : predefinition -> definition list = printf "Transforming definition:\n%s\n%!" (string_of_predefinition predef) ; def_of_predef_aux name [] preparams preproc - + diff --git a/src/REAMDE b/src/REAMDE new file mode 100644 index 0000000..8cc270e --- /dev/null +++ b/src/REAMDE @@ -0,0 +1,26 @@ + + +Les modules à modifier : + +Formula.formula_of_preformula : utilité à définir + +Control.handle_prop : définition d'une variable prop (une relation n-aire) +- On ajoute simplement une formule à l'environnement + + +Control.handle_local_check : +- effectue un check local pour vérifier la satisfiabilité de la formule +- dans l'article Model Checking Algorithms for the µ-calculus (CMU) + + +Control.handle_global_check : +- effectue un check global +- Topics in concurrency + + + + + + +TODO : + From 950ae1df4ab4a2e09ac9ddc2e99cacefc5ae42b2 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Thu, 10 Oct 2013 18:51:52 +0200 Subject: [PATCH 11/42] err commentaires Parser --- src/Parser.mly | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.mly b/src/Parser.mly index 4cd6d18..6730044 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -252,7 +252,7 @@ { raise (Fatal_Parse_Error "missing process for names") } | PROP IDENT LPAREN list_of_names RPAREN EQUAL formula - (* prop_name params fmla *) + /* prop_name params fmla */ { Control.handle_prop $2 $4 (formula_of_preformula $7) } | CHECK_LOCAL formula SATISFY process From 409117cbcd80eef32b6ee899fff43a3ff9f973ed Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Fri, 11 Oct 2013 11:41:45 +0200 Subject: [PATCH 12/42] Debut handle prop --- src/Control.ml | 11 +++++++++++ src/Formula.ml | 7 +++++-- src/Pave.ml | 2 ++ src/Utils.ml | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 2a174f9..f2780da 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -299,6 +299,17 @@ let handle_wderiv p = common_deriv (weak_derivatives false) printPfixMap "wderiv let handle_tderiv p = common_deriv (weak_derivatives true) printPfixMap "tderiv" "tau derivatives" p +let fetch_prop key = + Hashtbl.find global_proposition_map key + +let register_proposition prop = + Hashtbl.replace global_proposition_map (string_of_prop_header prop) prop + +let handle_prop prop = + if !script_mode then + printf "> %s\n%!" (string_of_definition def) ; + register_definition def; + printf "Definition '%s' registered\n%!" (def_name def) let handle_prop _ _ _ = assert false (* TODO *) diff --git a/src/Formula.ml b/src/Formula.ml index 2cbea2d..9f76a87 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -70,7 +70,10 @@ let rec string_of_formula : formula -> string = function | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) -let rec formula_of_preformula : formula -> formula = (* function +let rec formula_of_preformula : formula -> formula = fun f -> + printf "%s\n" @@ string_of_formula f; + raise Non_Implemented_Exception; + (* function | FTrue | FFalse | FAnd (f, g) @@ -81,5 +84,5 @@ let rec formula_of_preformula : formula -> formula = (* function | FProp (prop, params) | FVar var | FMu (x, f) - | FNu (x, f) -> *) assert false (*TODO*) + | FNu (x, f) -> *) diff --git a/src/Pave.ml b/src/Pave.ml index 11117c8..1ffc187 100644 --- a/src/Pave.ml +++ b/src/Pave.ml @@ -68,6 +68,8 @@ match !load_file with printf " ==> Undefined var \"%s\"\n%!" name | Presyntax.Typedef_Exception name -> printf " ==> Undefined type \"%s\"\n%!" name + | Utils.Non_Implemented_Exception -> + printf " ==> Unimplemented action\n%!" done | Some file -> printf "Loading file %s... \n%!" file; diff --git a/src/Utils.ml b/src/Utils.ml index 6b14ee0..6a3db14 100644 --- a/src/Utils.ml +++ b/src/Utils.ml @@ -3,6 +3,7 @@ open Printf (* globa parser exception *) exception Fatal_Parse_Error of string ;; +exception Non_Implemented_Exception (* string sets and maps *) From 1eb5d518c7864f789739099cc041466257239506 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Fri, 11 Oct 2013 12:39:35 +0200 Subject: [PATCH 13/42] type de handle_prop --- src/Control.ml | 2 +- src/Presyntax.ml | 15 ++++++++++----- src/REAMDE | 9 ++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 2a174f9..ca27094 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -301,7 +301,7 @@ let handle_tderiv p = common_deriv (weak_derivatives true) printPfixMap "tderiv" -let handle_prop _ _ _ = assert false (* TODO *) +let handle_prop ident nf fmla = assert false (* TODO *) let handle_check_local _ _ = assert false (* TODO *) diff --git a/src/Presyntax.ml b/src/Presyntax.ml index e5b85b5..8e75c47 100644 --- a/src/Presyntax.ml +++ b/src/Presyntax.ml @@ -297,7 +297,8 @@ and process_of_preprocess : preprocess -> process = | PRes (nvar, pproc) -> Res(nvar, (process_of_preprocess pproc) ) - | PCall( nom, pexprList) -> Call (nom, (List.map interprete_preexpr pexprList)) + | PCall( nom, pexprList) -> Call (nom, + (List.map interprete_preexpr pexprList)) | PRename( oldName, newName , pproc) -> Rename( oldName, newName, (process_of_preprocess pproc) ) @@ -338,9 +339,12 @@ let definitions_of_predefinition : predefinition -> definition list = let rec def_of_predef_aux name computed_params preparams preproc = match preparams with | [] -> [ Definition (name, computed_params, process_of_preprocess preproc) ] - | (PParamBool b)::tl -> def_of_predef_aux name (computed_params@[Bool b]) tl preproc - | (PParamName n)::tl -> def_of_predef_aux name (computed_params@[Name n]) tl preproc - | (PParamInt i)::tl -> def_of_predef_aux name (computed_params@[Int i]) tl preproc + | (PParamBool b)::tl -> def_of_predef_aux name + (computed_params@[Bool b]) tl preproc + | (PParamName n)::tl -> def_of_predef_aux name + (computed_params@[Name n]) tl preproc + | (PParamInt i)::tl -> def_of_predef_aux name + (computed_params@[Int i]) tl preproc | (PParamVar (nomVar, theType))::tl -> (if SMap.mem nomVar !env_var then raise @@ Vardef_Exception nomVar); @@ -355,7 +359,8 @@ let definitions_of_predefinition : predefinition -> definition list = (env_var := SMap.remove nomVar !env_var; List.flatten def_list) in - printf "Transforming definition:\n%s\n%!" (string_of_predefinition predef) ; + printf "Transforming definition:\n%s\n%!" + (string_of_predefinition predef) ; def_of_predef_aux name [] preparams preproc diff --git a/src/REAMDE b/src/REAMDE index 8cc270e..99eb14e 100644 --- a/src/REAMDE +++ b/src/REAMDE @@ -2,7 +2,9 @@ Les modules à modifier : -Formula.formula_of_preformula : utilité à définir +Formula.formula_of_preformula : +Conversion d'une formule de mu-calcul par valeur (ou pré-formule) en formule de mu-calcul pur (fonction formula_of_preformula.ml dans Formula.ml) + Control.handle_prop : définition d'une variable prop (une relation n-aire) - On ajoute simplement une formule à l'environnement @@ -10,12 +12,13 @@ Control.handle_prop : définition d'une variable prop (une relation n-aire) Control.handle_local_check : - effectue un check local pour vérifier la satisfiabilité de la formule -- dans l'article Model Checking Algorithms for the µ-calculus (CMU) +- Topics in concurrency : page 47, Chapter 4 Logics for processes +algo : page 61 to 68 Control.handle_global_check : - effectue un check global -- Topics in concurrency +- dans l'article Model Checking Algorithms for the µ-calculus (CMU) From ded8a9e480e35af9b074fb8ee6a09a22e25186ff Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Fri, 11 Oct 2013 12:15:01 +0200 Subject: [PATCH 14/42] Enregistrement des propositions; Ajout propositions sans arguments --- src/Control.ml | 13 +++++++------ src/Formula.ml | 18 +++++++++++++++--- src/Parser.mly | 3 +++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 49f04b8..083f9e8 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -2,6 +2,7 @@ open Printf open Utils open Syntax +open Formula open Normalize open Semop open Minim @@ -137,6 +138,8 @@ let handle_struct_congr p q = let global_definition_map = Hashtbl.create 64 +let global_proposition_map = Hashtbl.create 64 + let common_deriv f_deriv f_print str str2 p = if !script_mode then printf "> %s %s\n%!" str (string_of_process p) ; @@ -305,14 +308,12 @@ let fetch_prop key = let register_proposition prop = Hashtbl.replace global_proposition_map (string_of_prop_header prop) prop -let handle_prop prop = +let handle_prop name params formula = if !script_mode then - printf "> %s\n%!" (string_of_definition def) ; - register_definition def; - printf "Definition '%s' registered\n%!" (def_name def) - + printf "> %s\n%!" (string_of_formula formula) ; + register_proposition @@ Proposition(name, params, formula); + printf "Proposition '%s' registered\n%!" name -let handle_prop ident nf fmla = assert false (* TODO *) let handle_check_local _ _ = assert false (* TODO *) diff --git a/src/Formula.ml b/src/Formula.ml index 9f76a87..6db115a 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -5,6 +5,7 @@ open Printf open Presyntax open Utils + (* mu-calculus formulae *) type modality = @@ -43,6 +44,7 @@ let string_of_modality : modality -> string = function | FWInNecessity -> "[[?]]" | FWAnyNecessity -> "[[.]]" + type formula = | FTrue | FFalse @@ -69,10 +71,20 @@ let rec string_of_formula : formula -> string = function | FMu(x,f) -> sprintf "Mu(%s).%s" x (string_of_formula f) | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) +type proposition = Proposition of string * string list * formula + +let string_of_prop_header (Proposition(name, params, _)) = + name ^ (string_of_args (fun x -> x) params) + +let string_of_proposition = function + | (Proposition(_,_,formula)) as prop -> + "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) -let rec formula_of_preformula : formula -> formula = fun f -> - printf "%s\n" @@ string_of_formula f; - raise Non_Implemented_Exception; +let rec formula_of_preformula : formula -> formula = function + | _ as f -> + printf "Transforming %s\n" @@ string_of_formula f; + printf "Not implemented\n"; + f (* function | FTrue | FFalse diff --git a/src/Parser.mly b/src/Parser.mly index 6730044..72631bb 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -254,6 +254,9 @@ | PROP IDENT LPAREN list_of_names RPAREN EQUAL formula /* prop_name params fmla */ { Control.handle_prop $2 $4 (formula_of_preformula $7) } + | PROP IDENT EQUAL formula + /* prop_name fmla */ + { Control.handle_prop $2 [] (formula_of_preformula $4) } | CHECK_LOCAL formula SATISFY process { Control.handle_check_local (formula_of_preformula $2) (process_of_preprocess $4) } From 02cbd3c61f64584c0164aa83f2d8d15ad094728c Mon Sep 17 00:00:00 2001 From: remy Date: Mon, 14 Oct 2013 18:25:27 +0200 Subject: [PATCH 15/42] ajout handle prop --- src/Control.ml | 86 +++++++++++++++++++++++++++--------------- src/Formula.ml | 9 ++--- src/Pave.ml | 2 + src/{REAMDE => README} | 0 4 files changed, 61 insertions(+), 36 deletions(-) rename src/{REAMDE => README} (100%) diff --git a/src/Control.ml b/src/Control.ml index 083f9e8..5fa12f6 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -40,6 +40,13 @@ let script_mode = ref false ;; exception Constdef_Exception of string ;; exception Typedef_Exception of string ;; +type error = Unbound_Proposition of string +exception Error of error + +let print_error = function +| Unbound_Proposition s -> printf "unbound proposition %s" s + + let handle_help () = printf "%s\n> %!" help_me @@ -178,32 +185,32 @@ let dot_style_format' (pl, l, pl') = let common_lts f str p = if !script_mode then printf "> %s %s\n%!" str (string_of_process p) ; - let transs, time = timing (fun () -> f global_definition_map (normalize p)) - in - List.iter (fun t -> printf "%s\n" (string_of_transition t)) transs; - printf "\nGenerating %s.dot... %!" str; - let nprocs = - List.fold_left (fun acc (x, _, y) -> PSet.add x (PSet.add y acc)) - PSet.empty transs - in - let oc = open_out (sprintf "%s.dot" str ) in - fprintf oc "digraph LTS {\n"; - PSet.iter - (fun np -> - fprintf oc "\"%s\" [ fontcolor=blue ]\n" (string_of_nprocess np)) - nprocs; - if transs = [] then fprintf oc " 0\n" else - List.iter (fun t -> fprintf oc " %s\n" (dot_style_format t)) transs; - fprintf oc "}\n"; - close_out oc; - printf "done\n(elapsed time=%fs)\n%!" time +let transs, time = timing (fun () -> f global_definition_map (normalize p)) +in +List.iter (fun t -> printf "%s\n" (string_of_transition t)) transs; +printf "\nGenerating %s.dot... %!" str; +let nprocs = + List.fold_left (fun acc (x, _, y) -> PSet.add x (PSet.add y acc)) + PSet.empty transs +in +let oc = open_out (sprintf "%s.dot" str ) in +fprintf oc "digraph LTS {\n"; +PSet.iter + (fun np -> + fprintf oc "\"%s\" [ fontcolor=blue ]\n" (string_of_nprocess np)) + nprocs; +if transs = [] then fprintf oc " 0\n" else + List.iter (fun t -> fprintf oc " %s\n" (dot_style_format t)) transs; +fprintf oc "}\n"; +close_out oc; +printf "done\n(elapsed time=%fs)\n%!" time let common_minimization f_deriv str proc = - if !script_mode then - printf "> %s %s\n%!" str (string_of_process proc) ; - printf "Minimize process...\n%!"; - let transs, time = timing (fun () -> - let p = normalize proc in +if !script_mode then + printf "> %s %s\n%!" str (string_of_process proc) ; +printf "Minimize process...\n%!"; +let transs, time = timing (fun () -> + let p = normalize proc in minimize f_deriv global_definition_map p) in List.iter (fun t -> printf "%s\n" (string_of_transitions t)) transs; @@ -311,15 +318,32 @@ let register_proposition prop = let handle_prop name params formula = if !script_mode then printf "> %s\n%!" (string_of_formula formula) ; - register_proposition @@ Proposition(name, params, formula); + register_proposition @@ (name, params, formula); printf "Proposition '%s' registered\n%!" name -let handle_check_local _ _ = assert false (* TODO *) - -let handle_check_global _ _ = assert false (* TODO *) - - - +let handle_check_local f p = + let rec check = function + | FTrue -> true + | FFalse -> false + | FAnd (f, g) -> check f && check g + | FOr (f, g) -> check f || check g + | FImplies (f, g) -> check f |> not || check g + | FModal (m, f) -> assert false (* TODO *) + | FInvModal (m, f) -> assert false (* TODO *) + | FProp (prop, params) -> assert false (* TODO *) + | FVar var -> + begin try let name, params, _ = fetch_prop prop in + assert false (* TODO *) + with Not_found -> raise @@ Error (Unbound_Proposition prop) + end + | FMu (x, f) -> assert false (* TODO *) + | FNu (x, f) -> assert false (* TODO *) + in + let res = check f in + if res then printf "TRUE PROPERTY\n" + else printf "FALSE PROPERTY\n" + +let handle_check_global f p = assert false (* TODO *) diff --git a/src/Formula.ml b/src/Formula.ml index 6db115a..61e8c8e 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -71,14 +71,13 @@ let rec string_of_formula : formula -> string = function | FMu(x,f) -> sprintf "Mu(%s).%s" x (string_of_formula f) | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) -type proposition = Proposition of string * string list * formula +type proposition = string * string list * formula -let string_of_prop_header (Proposition(name, params, _)) = +let string_of_prop_header (name, params, _) = name ^ (string_of_args (fun x -> x) params) -let string_of_proposition = function - | (Proposition(_,_,formula)) as prop -> - "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) +let string_of_proposition ((_, _, formula) as prop) = + "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) let rec formula_of_preformula : formula -> formula = function | _ as f -> diff --git a/src/Pave.ml b/src/Pave.ml index 1ffc187..2400f2e 100644 --- a/src/Pave.ml +++ b/src/Pave.ml @@ -70,6 +70,8 @@ match !load_file with printf " ==> Undefined type \"%s\"\n%!" name | Utils.Non_Implemented_Exception -> printf " ==> Unimplemented action\n%!" + | Control.Error e -> Control.print_error e + done | Some file -> printf "Loading file %s... \n%!" file; diff --git a/src/REAMDE b/src/README similarity index 100% rename from src/REAMDE rename to src/README From 36313a2702e390a09f2cd2c906b2270e0a8f94cd Mon Sep 17 00:00:00 2001 From: remy Date: Tue, 15 Oct 2013 13:08:59 +0200 Subject: [PATCH 16/42] =?UTF-8?q?corrections=20pour=20que=20=C3=A7a=20comp?= =?UTF-8?q?ile=20~~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Control.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 5fa12f6..5fa1fdf 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -333,10 +333,10 @@ let handle_check_local f p = | FInvModal (m, f) -> assert false (* TODO *) | FProp (prop, params) -> assert false (* TODO *) | FVar var -> - begin try let name, params, _ = fetch_prop prop in + (* begin try let name, params, _ = fetch_prop prop in *) assert false (* TODO *) - with Not_found -> raise @@ Error (Unbound_Proposition prop) - end + (* with Not_found -> raise @@ Error (Unbound_Proposition prop) *) + (* end *) | FMu (x, f) -> assert false (* TODO *) | FNu (x, f) -> assert false (* TODO *) in From 11ef32181840d102a1f11240995c603a2f49d60e Mon Sep 17 00:00:00 2001 From: EL SIBAIE BESOGNET REMY <3361547@ari-41-307-06.infop6.jussieu.fr> Date: Tue, 15 Oct 2013 17:10:30 +0200 Subject: [PATCH 17/42] refactor du type modalitycorrection de l'indentation --- src/Control.ml | 227 ++++++++++++++++++++++++++++++++----------------- src/Formula.ml | 53 ++++-------- src/Minim.ml | 142 +++++++++++++++---------------- src/Parser.mly | 39 ++++----- src/Semop.ml | 151 +++++++++++++++----------------- src/Utils.ml | 8 ++ 6 files changed, 335 insertions(+), 285 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 5fa1fdf..7ca114b 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -44,7 +44,7 @@ type error = Unbound_Proposition of string exception Error of error let print_error = function -| Unbound_Proposition s -> printf "unbound proposition %s" s + | Unbound_Proposition s -> printf "unbound proposition %s" s let handle_help () = @@ -74,22 +74,22 @@ let handle_typedef_range (type_name:string) (min_val:string) (max_val:string) = if not (SMap.mem type_name !Presyntax.env_type) then let find_val v = try - SMap.find v !Presyntax.env_const + SMap.find v !Presyntax.env_const with - Not_found -> - try - int_of_string v - with - Failure _ -> raise (Typedef_Exception type_name) + Not_found -> + try + int_of_string v + with + Failure _ -> raise (Typedef_Exception type_name) in let min = find_val min_val and max = find_val max_val in Presyntax.add_to_env_type type_name ( if min < max then - Presyntax.PTDefRange (type_name, min, max) - else - Presyntax.PTDefRange (type_name, max, min) + Presyntax.PTDefRange (type_name, min, max) + else + Presyntax.PTDefRange (type_name, max, min) ) else raise (Typedef_Exception type_name) @@ -185,32 +185,32 @@ let dot_style_format' (pl, l, pl') = let common_lts f str p = if !script_mode then printf "> %s %s\n%!" str (string_of_process p) ; -let transs, time = timing (fun () -> f global_definition_map (normalize p)) -in -List.iter (fun t -> printf "%s\n" (string_of_transition t)) transs; -printf "\nGenerating %s.dot... %!" str; -let nprocs = - List.fold_left (fun acc (x, _, y) -> PSet.add x (PSet.add y acc)) - PSet.empty transs -in -let oc = open_out (sprintf "%s.dot" str ) in -fprintf oc "digraph LTS {\n"; -PSet.iter - (fun np -> - fprintf oc "\"%s\" [ fontcolor=blue ]\n" (string_of_nprocess np)) - nprocs; -if transs = [] then fprintf oc " 0\n" else - List.iter (fun t -> fprintf oc " %s\n" (dot_style_format t)) transs; -fprintf oc "}\n"; -close_out oc; -printf "done\n(elapsed time=%fs)\n%!" time + let transs, time = timing (fun () -> f global_definition_map (normalize p)) + in + List.iter (fun t -> printf "%s\n" (string_of_transition t)) transs; + printf "\nGenerating %s.dot... %!" str; + let nprocs = + List.fold_left (fun acc (x, _, y) -> PSet.add x (PSet.add y acc)) + PSet.empty transs + in + let oc = open_out (sprintf "%s.dot" str ) in + fprintf oc "digraph LTS {\n"; + PSet.iter + (fun np -> + fprintf oc "\"%s\" [ fontcolor=blue ]\n" (string_of_nprocess np)) + nprocs; + if transs = [] then fprintf oc " 0\n" else + List.iter (fun t -> fprintf oc " %s\n" (dot_style_format t)) transs; + fprintf oc "}\n"; + close_out oc; + printf "done\n(elapsed time=%fs)\n%!" time let common_minimization f_deriv str proc = -if !script_mode then - printf "> %s %s\n%!" str (string_of_process proc) ; -printf "Minimize process...\n%!"; -let transs, time = timing (fun () -> - let p = normalize proc in + if !script_mode then + printf "> %s %s\n%!" str (string_of_process proc) ; + printf "Minimize process...\n%!"; + let transs, time = timing (fun () -> + let p = normalize proc in minimize f_deriv global_definition_map p) in List.iter (fun t -> printf "%s\n" (string_of_transitions t)) transs; @@ -233,30 +233,31 @@ let transs, time = timing (fun () -> let common_bisim f_bisim str str2 str3 p1 p2 = if !script_mode then - printf "> %s %s ~ %s\n%!" str (string_of_process p1) (string_of_process p2) ; + printf "> %s %s ~ %s\n%!" str (string_of_process p1) + (string_of_process p2) ; printf "Calculate %s...\n%!" str2; let start_time = Sys.time() in let np1 = normalize p1 in let np2 = normalize p2 in try - let bsm = f_bisim global_definition_map np1 np2 - in - let end_time = Sys.time() - in + let bsm = f_bisim global_definition_map np1 np2 in + let end_time = Sys.time() in let print (np1, np2) = printf "{ %s ; %s }\n" (string_of_nprocess np1) (string_of_nprocess np2) in - printf "the processes are %s\n(elapsed time=%fs)\n%!" str3 (end_time-.start_time) ; + printf "the processes are %s\n(elapsed time=%fs)\n%!" str3 + (end_time-.start_time) ; BSet.iter print bsm with Failure "Not bisimilar" -> - let end_time = Sys.time() - in - printf "the processes are *not* %s\n(elapsed time=%fs)\n%!" str3 (end_time-.start_time) + let end_time = Sys.time() in + printf "the processes are *not* %s\n(elapsed time=%fs)\n%!" + str3 (end_time-.start_time) let common_is_bisim f_bisim str str2 p1 p2 = if !script_mode then - printf "> %s ? %s ~ %s\n%!" str (string_of_process p1) (string_of_process p2) ; + printf "> %s ? %s ~ %s\n%!" str + (string_of_process p1) (string_of_process p2) ; let ok,time = timing (fun () -> let np1 = normalize p1 in let np2 = normalize p2 in @@ -267,8 +268,9 @@ let common_is_bisim f_bisim str str2 p1 p2 = else printf "the processes are *not* %s\n(elapsed time=%fs)\n%!" str2 time let common_is_fbisim f_deriv str1 str2 p1 p2 = -if !script_mode then - printf "> %s ? %s ~ %s\n%!" str1 (string_of_process p1) (string_of_process p2) ; + if !script_mode then + printf "> %s ? %s ~ %s\n%!" str1 + (string_of_process p1) (string_of_process p2) ; let ok,time = timing (fun () -> let np1 = normalize p1 in let np2 = normalize p2 in @@ -287,27 +289,42 @@ let handle_wlts p = common_lts (lts (weak_transitions false)) "wlts" p let handle_minimization p = common_minimization derivatives "mini" p -let handle_wminimization p = common_minimization (weak_transitions false) "wmini" p +let handle_wminimization p = common_minimization + (weak_transitions false) "wmini" p + + +let handle_bisim p1 p2 = common_bisim construct_bisimilarity + "bisim" "bisimilarity" "bisimilar" p1 p2 + +let handle_wbisim p1 p2 = common_bisim construct_weak_bisimilarity + "wbisim" "weak bisimilarity" "weakly bisimilar" p1 p2 -let handle_bisim p1 p2 = common_bisim construct_bisimilarity "bisim" "bisimilarity" "bisimilar" p1 p2 +let handle_is_bisim p1 p2 = common_is_bisim is_bisimilar + "bisim" "bisimilar" p1 p2 -let handle_wbisim p1 p2 = common_bisim construct_weak_bisimilarity "wbisim" "weak bisimilarity" "weakly bisimilar" p1 p2 +let handle_is_fbisim p1 p2 = common_is_fbisim derivatives + "fbisim" "bisimilar" p1 p2 +let handle_is_wbisim p1 p2 = common_is_bisim is_weakly_bisimilar + "wbisim" "weakly bisimilar" p1 p2 -let handle_is_bisim p1 p2 = common_is_bisim is_bisimilar "bisim" "bisimilar" p1 p2 +let handle_is_fwbisim p1 p2 = common_is_fbisim (weak_transitions false) + "wfbisim" "weakly bisimilar" p1 p2 -let handle_is_fbisim p1 p2 = common_is_fbisim derivatives "fbisim" "bisimilar" p1 p2 +let handle_deriv p = common_deriv derivatives (TSet.iter (fun t -> + printf "%s\n" (string_of_derivative t))) "deriv" "derivatives" p -let handle_is_wbisim p1 p2 = common_is_bisim is_weakly_bisimilar "wbisim" "weakly bisimilar" p1 p2 +let handle_wderiv p = common_deriv (weak_derivatives false) + printPfixMap "wderiv" "weak derivatives" p -let handle_is_fwbisim p1 p2 = common_is_fbisim (weak_transitions false) "wfbisim" "weakly bisimilar" p1 p2 +let handle_tderiv p = common_deriv (weak_derivatives true) + printPfixMap "tderiv" "tau derivatives" p -let handle_deriv p = common_deriv derivatives (TSet.iter (fun t -> printf "%s\n" (string_of_derivative t))) "deriv" "derivatives" p -let handle_wderiv p = common_deriv (weak_derivatives false) printPfixMap "wderiv" "weak derivatives" p -let handle_tderiv p = common_deriv (weak_derivatives true) printPfixMap "tderiv" "tau derivatives" p +(** Mu Calculus *) + let fetch_prop key = Hashtbl.find global_proposition_map key @@ -322,28 +339,86 @@ let handle_prop name params formula = printf "Proposition '%s' registered\n%!" name -let handle_check_local f p = - let rec check = function - | FTrue -> true - | FFalse -> false - | FAnd (f, g) -> check f && check g - | FOr (f, g) -> check f || check g - | FImplies (f, g) -> check f |> not || check g - | FModal (m, f) -> assert false (* TODO *) - | FInvModal (m, f) -> assert false (* TODO *) - | FProp (prop, params) -> assert false (* TODO *) - | FVar var -> - (* begin try let name, params, _ = fetch_prop prop in *) - assert false (* TODO *) - (* with Not_found -> raise @@ Error (Unbound_Proposition prop) *) - (* end *) - | FMu (x, f) -> assert false (* TODO *) - | FNu (x, f) -> assert false (* TODO *) + +(* + +Dans checklocal : + - on normalise le proc + - une fois dans Fmodal : on appelle next_matching_process_set et on + appelle check sur l'ensemble résultat +*) + +let transitions_of nproc = derivatives global_definition_map nproc + +(* Semop.PSet : set de processus *) + +let next_matching_process_set modality nproc = + let ts = transitions_of nproc in + TSet.fold (fun t set -> + let _, mod_to_check, destination = t in + let it_matches = + match modality, mod_to_check with + | _ , T_Tau -> true + | _ -> assert false + in + if it_matches then PSet.add destination set else set + ) ts PSet.empty + + +(* +mod_to_check : +type label = T_Tau | T_In of name | T_Out of name + +W: weak +Possibly : <> +Necessity : [] + +modality : + FPossibly(acts) : liste d'action dans un <> + FOutPossibly : sortie dans un <> + FInPossibly "" + FAnyPossibly "<.>" + FWPossibly(acts) "<<" ">>" "," string_of_preprefix acts + FWOutPossibly "<>" + FWInPossibly "<>" + FWAnyPossibly "<<.>>" + FNecessity(acts) s"[" "]" "," string_of_preprefix acts + FOutNecessity "[!]" + FInNecessity "[?]" + FAnyNecessity "[.]" + FWNecessity(acts)"[[" "]]" "," string_of_preprefix acts + FWOutNecessity "[[!]]" + FWInNecessity "[[?]]" + FWAnyNecessity "[[.]]" *) + + +let handle_check_local formula proc = + let nproc = normalize proc in + let rec check formula p = + match formula with + | FTrue -> true + | FFalse -> false + | FAnd (f1, f2) -> check f1 p && check f2 p + | FOr (f1, f2) -> check f1 p || check f2 p + | FImplies (f1, f2) -> check f1 p |> not || check f2 p + | FModal (modality, formula) -> + PSet.for_all (check formula) @@ next_matching_process_set modality p + (* transitions : [a] *) + (* | FInvModal (modality, formula) -> assert false (\* TODO *\) *) + (* transitions : not ou not [a] *) + | _-> assert false + (* | FProp (prop, params) -> assert false (\* TODO *\) *) + (* | FVar var -> *) + (* (\* begin try let name, params, _ = fetch_prop prop in *\) *) + (* assert false (\* TODO *\) *) + (* (\* with Not_found -> raise @@ Error (Unbound_Proposition prop) *\) *) + (* (\* end *\) *) + (* | FMu (x, f) -> assert false (\* TODO *\) *) + (* | FNu (x, f) -> assert false (\* TODO *\) *) in - let res = check f in + let res = check formula nproc in if res then printf "TRUE PROPERTY\n" else printf "FALSE PROPERTY\n" - + let handle_check_global f p = assert false (* TODO *) - diff --git a/src/Formula.ml b/src/Formula.ml index 61e8c8e..6c1976b 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -8,43 +8,24 @@ open Utils (* mu-calculus formulae *) -type modality = - | FPossibly of preprefix list - | FOutPossibly - | FInPossibly - | FAnyPossibly - | FWPossibly of preprefix list - | FWOutPossibly - | FWInPossibly - | FWAnyPossibly - | FNecessity of preprefix list - | FOutNecessity - | FInNecessity - | FAnyNecessity - | FWNecessity of preprefix list - | FWOutNecessity - | FWInNecessity - | FWAnyNecessity - -let string_of_modality : modality -> string = function - | FPossibly(acts) -> string_of_collection "<" ">" "," string_of_preprefix acts - | FOutPossibly -> "" - | FInPossibly -> "" - | FAnyPossibly -> "<.>" - | FWPossibly(acts) -> string_of_collection "<<" ">>" "," string_of_preprefix acts - | FWOutPossibly -> "<>" - | FWInPossibly -> "<>" - | FWAnyPossibly -> "<<.>>" - | FNecessity(acts) -> string_of_collection "[" "]" "," string_of_preprefix acts - | FOutNecessity -> "[!]" - | FInNecessity -> "[?]" - | FAnyNecessity -> "[.]" - | FWNecessity(acts) -> string_of_collection "[[" "]]" "," string_of_preprefix acts - | FWOutNecessity -> "[[!]]" - | FWInNecessity -> "[[?]]" - | FWAnyNecessity -> "[[.]]" - +type restr = Rin | Rout | Rany | Rpref of preprefix list +type existence = Possibly | Necessity +type strength = Weak | Strong +type modality = strength * existence * restr + +let string_of_restr = function + | Rout -> "!" + | Rin -> "?" + | Rany -> "." + | Rpref acts -> string_of_collection_no_block "," string_of_preprefix acts +let to_string_of_existence = function Possibly -> sprintf "<%s>" + | Necessity -> sprintf "[%s]" +let to_string_of_strongness f r = function Weak -> f (f r) + | Strong -> f r +let string_of_modality (s, e, r) = + string_of_strongness (string_of_existence e) (string_of_restr r) + type formula = | FTrue | FFalse diff --git a/src/Minim.ml b/src/Minim.ml index 8b59e81..a272f87 100755 --- a/src/Minim.ml +++ b/src/Minim.ml @@ -26,6 +26,7 @@ module GSet = Set.Make ( type transitions = nprocess list * label * nprocess list + (* transition set *) module TsSet = Set.Make ( struct type t = transitions @@ -50,26 +51,26 @@ let string_of_graph g = acc ^ (sprintf "%s has prevs %s\n" (string_of_gstate dst) (string_of_gset string_of_gstate prevs)) in - GMap.fold folder g "" + GMap.fold folder g "" let string_of_partition parts = (List.fold_left - (fun s part -> s ^ "\n" ^ (string_of_gset string_of_gstate part)) "" parts) - ^ "\n" + (fun s part -> s ^ "\n" ^ (string_of_gset string_of_gstate part)) "" parts) + ^ "\n" let build_graph f_deriv init_graph init_partition defs ps = let rec add_to_partition part elem = match part with - | [] -> [GSet.singleton elem] - | set::ss -> - begin - match (GSet.choose set, elem) with - | (PState _, PState _) -> - (GSet.add elem set)::ss - | (LState(_,a,_), LState(_,b,_)) when a = b -> - (GSet.add elem set)::ss - | _ -> set::(add_to_partition ss elem) - end + | [] -> [GSet.singleton elem] + | set::ss -> + begin + match (GSet.choose set, elem) with + | (PState _, PState _) -> + (GSet.add elem set)::ss + | (LState(_,a,_), LState(_,b,_)) when a = b -> + (GSet.add elem set)::ss + | _ -> set::(add_to_partition ss elem) + end in let rec f (graph, part) procs_todo procs_done = try @@ -97,16 +98,16 @@ let build_graph f_deriv init_graph init_partition defs ps = PSet.remove p (PSet.union (PSet.diff next_procs procs_done) procs_todo) in let new_procs_done = PSet.add p procs_done in - f (new_graph, new_part) new_procs_todo new_procs_done + f (new_graph, new_part) new_procs_todo new_procs_done with Not_found -> (graph, part) in match ps with - | [] -> (init_graph, init_partition) - | p1::[] -> f (init_graph, init_partition) (PSet.singleton p1) PSet.empty - | p1::p2::[] -> - let tmp = f (init_graph, init_partition) (PSet.singleton p1) PSet.empty - in f tmp (PSet.singleton p2) PSet.empty - | _ -> (init_graph, init_partition) + | [] -> (init_graph, init_partition) + | p1::[] -> f (init_graph, init_partition) (PSet.singleton p1) PSet.empty + | p1::p2::[] -> + let tmp = f (init_graph, init_partition) (PSet.singleton p1) PSet.empty + in f tmp (PSet.singleton p2) PSet.empty + | _ -> (init_graph, init_partition) let rec refine (graph, part) = let prevs = @@ -121,16 +122,16 @@ let rec refine (graph, part) = in let rec f1 pt pr = match pt with - | [] -> [] - | h1::t1 -> (f2 h1 pr)@(f1 t1 pr) + | [] -> [] + | h1::t1 -> (f2 h1 pr)@(f1 t1 pr) and f2 pt pr = match pr with - | [] -> [pt] - | h2::t2 -> let (spl1, spl2) = split pt h2 in - if GSet.is_empty spl1 || GSet.is_empty spl2 then - f2 pt t2 - else - [spl1 ; spl2] + | [] -> [pt] + | h2::t2 -> let (spl1, spl2) = split pt h2 in + if GSet.is_empty spl1 || GSet.is_empty spl2 then + f2 pt t2 + else + [spl1 ; spl2] in let part' = f1 part prevs in if (List.length part = List.length part') @@ -139,51 +140,50 @@ let rec refine (graph, part) = let build_lts partition = let (ps, ls) = List.partition (fun x -> match GSet.choose x with - | LState _ -> false - | PState _ -> true) + | LState _ -> false + | PState _ -> true) partition in let pstates = List.map (fun set -> - GSet.fold - (fun x acc -> match x with - | PState (_, np) -> np::acc - | LState _ -> acc) set [] + GSet.fold + (fun x acc -> match x with + | PState (_, np) -> np::acc + | LState _ -> acc) set [] ) ps and lstates = List.map (fun set -> - GSet.fold - (fun x acc -> match x with - | PState _ -> acc - | LState t -> t::acc) set [] + GSet.fold + (fun x acc -> match x with + | PState _ -> acc + | LState t -> t::acc) set [] ) ls in let rec f transs todos = match todos with - | [] -> transs - | cp::t -> - begin - let p = List.hd cp in - let labs = - List.fold_left - (fun acc x -> - match List.filter (fun (src,_,_) -> src = p) x with - | [] -> acc - | t::_ -> t::acc - ) - [] lstates - in let transs' = - List.fold_left - (fun acc (_,lbl,dst) -> - let cdl = List.filter - (fun x -> List.mem dst x) pstates in - assert ((List.length cdl) = 1); - let cd = List.hd cdl in - TsSet.add (cp, lbl, cd) acc - ) transs labs - in f transs' t - end - in - f TsSet.empty pstates + | [] -> transs + | cp::t -> + begin + let p = List.hd cp in + let labs = + List.fold_left + (fun acc x -> + match List.filter (fun (src,_,_) -> src = p) x with + | [] -> acc + | t::_ -> t::acc + ) + [] lstates + in let transs' = + List.fold_left + (fun acc (_,lbl,dst) -> + let cdl = List.filter + (fun x -> List.mem dst x) pstates in + assert ((List.length cdl) = 1); + let cd = List.hd cdl in + TsSet.add (cp, lbl, cd) acc + ) transs labs + in f transs' t + end + in f TsSet.empty pstates let minimize f_deriv defs proc = @@ -191,14 +191,14 @@ let minimize f_deriv defs proc = let init_partition = [GSet.singleton (PState(false,proc))] in let (graph, partition) = build_graph f_deriv init_graph init_partition defs [proc] in (*print_endline "STATE GRAPH :"; - print_string (string_of_graph graph); - print_string "PARTITION :"; - print_string (string_of_partition partition);*) - let partition' = refine (graph, partition) in - (*print_string "PARTITION REFINED :"; - print_string (string_of_partition partition');*) - let transitions = build_lts partition' in - TsSet.fold (fun t acc -> t :: acc) transitions [] + print_string (string_of_graph graph); + print_string "PARTITION :"; + print_string (string_of_partition partition);*) + let partition' = refine (graph, partition) in + (*print_string "PARTITION REFINED :"; + print_string (string_of_partition partition');*) + let transitions = build_lts partition' in + TsSet.fold (fun t acc -> t :: acc) transitions [] let is_fbisimilar f_deriv defs p1 p2 = let root = PState(true,(SSet.empty,NSilent)) in diff --git a/src/Parser.mly b/src/Parser.mly index 72631bb..3d885dd 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -375,25 +375,26 @@ | IDENT { FVar($1) } modality: - | INF list_of_prefixes SUP { FPossibly $2 } - | INF OUT SUP { FOutPossibly } - | INF IN SUP { FInPossibly } - | INF DOT SUP { FAnyPossibly } - - | INF INF list_of_prefixes SUP SUP { FWPossibly $3 } - | INF INF OUT SUP SUP { FWOutPossibly } - | INF INF IN SUP SUP { FWInPossibly } - | INF INF DOT SUP SUP { FWAnyPossibly } - - | LBRACKET list_of_prefixes RBRACKET { FNecessity $2 } - | LBRACKET OUT RBRACKET { FOutNecessity } - | LBRACKET IN RBRACKET { FInNecessity } - | LBRACKET DOT RBRACKET { FAnyNecessity } - - | LBRACKET LBRACKET list_of_prefixes RBRACKET RBRACKET { FWNecessity $3 } - | LBRACKET LBRACKET OUT RBRACKET RBRACKET { FWOutNecessity } - | LBRACKET LBRACKET IN RBRACKET RBRACKET { FWInNecessity } - | LBRACKET LBRACKET DOT RBRACKET RBRACKET { FWAnyNecessity } + | INF list_of_prefixes SUP { Strong, Possibly, Rpref $2 } + | INF OUT SUP { Strong, Possibly, Rout } + | INF IN SUP { Strong, Possibly, Rin } + | INF DOT SUP { Strong, Possibly, Rany } + + | INF INF list_of_prefixes SUP SUP { Weak, Possibly, Rpref $3 } + | INF INF OUT SUP SUP { Weak, Possibly, Rout } + | INF INF IN SUP SUP { Weak, Possibly, Rin } + | INF INF DOT SUP SUP { Weak, Possibly, Rany } + + | LBRACKET list_of_prefixes RBRACKET { Strong, Necessity, Rpref $2 } + | LBRACKET OUT RBRACKET { Strong, Necessity, Rout } + | LBRACKET IN RBRACKET { Strong, Necessity, Rin} + | LBRACKET DOT RBRACKET { Strong, Necessity, Rany } + + | LBRACKET LBRACKET list_of_prefixes RBRACKET RBRACKET { + Weak, Necessity, Rpref $3 } + | LBRACKET LBRACKET OUT RBRACKET RBRACKET { Weak, Necessity, Rout } + | LBRACKET LBRACKET IN RBRACKET RBRACKET { Weak, Necessity, Rin } + | LBRACKET LBRACKET DOT RBRACKET RBRACKET { Weak, Necessity, Rany } %% diff --git a/src/Semop.ml b/src/Semop.ml index 046c268..ec4478c 100644 --- a/src/Semop.ml +++ b/src/Semop.ml @@ -54,7 +54,7 @@ module BSet = Set.Make ( let label_of_prefix =function | Tau -> T_Tau | In n -> T_In n | Out n -> T_Out n - + let derivatives defs ((orig_res, orig_np) as orig_nproc) = let restrict res derivs = @@ -62,8 +62,7 @@ let derivatives defs ((orig_res, orig_np) as orig_nproc) = match lab with | T_Tau -> true | T_In n | T_Out n -> not (SSet.mem n res) - in - TSet.filter filter derivs + in TSet.filter filter derivs in let renames var name derivs= let folder (src, lab, (dres,dest)) acc = @@ -71,10 +70,8 @@ let derivatives defs ((orig_res, orig_np) as orig_nproc) = | T_Tau -> T_Tau | T_In n -> T_In (if n = var then name else n) | T_Out n -> T_Out (if n = var then name else n) - in - TSet.add (src, lab', (dres,NRename(var,name,dest))) acc - in - TSet.fold folder derivs TSet.empty + in TSet.add (src, lab', (dres,NRename(var,name,dest))) acc + in TSet.fold folder derivs TSet.empty in let rec f res np = match np with @@ -87,13 +84,13 @@ let derivatives defs ((orig_res, orig_np) as orig_nproc) = in let (body_res, body_np) = normalize body in let derivs = f body_res body_np in - restrict body_res derivs + restrict body_res derivs | NPrefix (pref, np) -> TSet.singleton (orig_nproc, label_of_prefix pref, renormalize(res,np)) | NRename (var,name,np) -> let derivs = f res np in - renames var name derivs + renames var name derivs | NSum nps -> List.fold_left (fun acc np -> TSet.union (f res np) acc) TSet.empty nps @@ -113,48 +110,42 @@ let derivatives defs ((orig_res, orig_np) as orig_nproc) = (*compteur, accu processus, accu restrictions*) let rec gen_name n = let new_n = "f" ^ (string_of_int n) in - if SSet.mem new_n in_frees || SSet.mem new_n in_res - then gen_name (succ n) - else (succ n, new_n) + if SSet.mem new_n in_frees || SSet.mem new_n in_res + then gen_name (succ n) + else (succ n, new_n) in let (new_cnt, new_name) = gen_name cnt in (new_cnt, nproc_subst acc_in name new_name, SSet.add new_name (SSet.remove name acc_in_res)) (* nproc_subst renames the first encountered label != Tau - or the name in a Rename node + or the name in a Rename node We delete the passage and we rename name by new_name in the set - *) - in + *) in let (_, new_in, new_in_res) = SSet.fold folder_in in_forbid (0, dst, in_res) - (*We rename all the names (at depth 1) - of dst in folder_in by the new name - *) - in - let folder_oths name (cnt, acc_oths, acc_oths_res) = - let rec gen_name n = - let new_n = "f" ^ (string_of_int n) in - if SSet.mem new_n np_frees || SSet.mem new_n res - then gen_name (succ n) - else (succ n, new_n) - in - let (new_cnt, new_name) = gen_name cnt in - (new_cnt, - List.map (fun np -> nproc_subst np name new_name) - acc_oths, - SSet.add new_name (SSet.remove name acc_oths_res)) - in - let (_, new_oths, new_oths_res) = - SSet.fold folder_oths np_forbid (0, oths_np, res) - in - renormalize (SSet.union new_in_res new_oths_res, - NPar (new_in :: new_oths)) - in - TSet.add (src, lab, new_dst) acc - in - (TSet.fold folder nexts acc_par, (nexts, oths_np) :: acc_simpl) - in - List.fold_left folder (TSet.empty, []) nps + (*We rename all the names (at depth 1) + of dst in folder_in by the new name + *) + in let folder_oths name (cnt, acc_oths, acc_oths_res) = + let rec gen_name n = + let new_n = "f" ^ (string_of_int n) in + if SSet.mem new_n np_frees || SSet.mem new_n res + then gen_name (succ n) + else (succ n, new_n) + in + let (new_cnt, new_name) = gen_name cnt in + (new_cnt, + List.map (fun np -> nproc_subst np name new_name) + acc_oths, + SSet.add new_name (SSet.remove name acc_oths_res)) + in + let (_, new_oths, new_oths_res) = + SSet.fold folder_oths np_forbid (0, oths_np, res) + in renormalize (SSet.union new_in_res new_oths_res, + NPar (new_in :: new_oths)) + in TSet.add (src, lab, new_dst) acc + in (TSet.fold folder nexts acc_par, (nexts, oths_np) :: acc_simpl) + in List.fold_left folder (TSet.empty, []) nps in let folder ((map, _) as acc) (elt_set, oths) = let folder' (_, lab, dst) ((map', set') as acc') = @@ -163,40 +154,34 @@ let derivatives defs ((orig_res, orig_np) as orig_nproc) = try SMap.add org ((dst,oths) :: SMap.find org map') map' with Not_found -> SMap.add org [(dst,oths)] map' in - try - let dsts' = SMap.find opp map in - let folder acc (np, oths') = - let oths_np = - List.filter (fun e -> List.memq e oths) oths' - in - let nproc' = - let p1 = denormalize np in - let p2 = denormalize dst in - let oths_p = - List.map (fun np -> denormalize (res, np)) oths_np - in - let p = - List.fold_left (fun acc p -> Par (p, acc)) Silent - (p1 :: p2 :: oths_p) - in - normalize p - in - TSet.add (orig_nproc, T_Tau, nproc') acc + try + let dsts' = SMap.find opp map in + let folder acc (np, oths') = + let oths_np = + List.filter (fun e -> List.memq e oths) oths' in - (new_map', List.fold_left folder set' dsts') - with Not_found -> (new_map', set') - in - match lab with - | T_Tau -> acc' - | T_In n -> add_taus (n ^ "?") (n ^ "!") - | T_Out n -> add_taus (n ^ "!") (n ^ "?") - in - TSet.fold folder' elt_set acc - in - snd (List.fold_left folder (SMap.empty, set_par) set_simpl) + let nproc' = + let p1 = denormalize np in + let p2 = denormalize dst in + let oths_p = + List.map (fun np -> denormalize (res, np)) oths_np + in + let p = + List.fold_left (fun acc p -> Par (p, acc)) Silent + (p1 :: p2 :: oths_p) + in normalize p + in TSet.add (orig_nproc, T_Tau, nproc') acc + in (new_map', List.fold_left folder set' dsts') + with Not_found -> (new_map', set') + in match lab with + | T_Tau -> acc' + | T_In n -> add_taus (n ^ "?") (n ^ "!") + | T_Out n -> add_taus (n ^ "!") (n ^ "?") + in TSet.fold folder' elt_set acc + in snd (List.fold_left folder (SMap.empty, set_par) set_simpl) in let derivs = f orig_res orig_np in - restrict orig_res derivs + restrict orig_res derivs ;; let lts deriv_f defs p = @@ -239,13 +224,13 @@ let construct_bisimilarity defs nproc1 nproc2 = try construct (BSet.add dsts acc_bsm) dst1 dst2 with Failure "Bad path" -> search (TSet.remove ty acc_dys) in - search dys + search dys in - TSet.fold (folder d2s false) d1s - (TSet.fold (folder d1s true) d2s bsm) + TSet.fold (folder d2s false) d1s + (TSet.fold (folder d1s true) d2s bsm) in - try construct (BSet.singleton (nproc1, nproc2)) nproc1 nproc2 - with Failure "Bad path" -> failwith "Not bisimilar" + try construct (BSet.singleton (nproc1, nproc2)) nproc1 nproc2 + with Failure "Bad path" -> failwith "Not bisimilar" ;; let is_bisimilar defs nproc1 nproc2 = @@ -294,7 +279,7 @@ let is_restricted rest pf = let prefix_of_label = function | T_Tau -> Tau | T_In n -> In n | T_Out n -> Out n - + let weak_derivatives tau_only defs (orig_restrict, p) : PSet.t PrefixMap.t = let rec weak_deriv_aux pfix_key entry_map (restrict, p) = @@ -373,8 +358,8 @@ let weak_derivatives tau_only defs (orig_restrict, p) : PSet.t PrefixMap.t = let (body_res, body_np) = normalize body in let derivs = weak_deriv_aux pfix_key entry_map ((SSet.union restrict body_res), body_np) in PrefixMap.filter (fun k _ -> not (is_restricted body_res k)) derivs - (* on ne filtre que sur body_res -> restrict sera au pire filtré à la sortie - * de weak_derivatives, et plus d'états dans la map peut empêcher des récursions *) + (* on ne filtre que sur body_res -> restrict sera au pire filtré à la sortie + * de weak_derivatives, et plus d'états dans la map peut empêcher des récursions *) | NRename (old, newn, p') -> (*ici on fait un appel réccursif avec une map vide et on trie à la sortie*) let m = weak_deriv_aux pfix_key PrefixMap.empty (restrict, p') @@ -408,7 +393,7 @@ let weak_derivatives tau_only defs (orig_restrict, p) : PSet.t PrefixMap.t = let derivs = PrefixMap.filter (fun k _ -> not (is_restricted orig_restrict k)) derivs in pmap_add_val derivs Tau (orig_restrict, p) - + let printPfixMap map = let b = PrefixMap.bindings map in diff --git a/src/Utils.ml b/src/Utils.ml index 6a3db14..b3f08ca 100644 --- a/src/Utils.ml +++ b/src/Utils.ml @@ -23,6 +23,14 @@ let string_of_collection (op:string) (cl:string) (sep:string) in op ^ (str lst) ^ cl +let string_of_collection_no_block (sep:string) + (tostr: 'a -> string) (lst: 'a list) = + let rec str = function + | [] -> "" + | e::[] -> tostr e + | e::es -> (tostr e) ^ sep ^ (str es) + in str lst + let string_of_list tostr lst = string_of_collection "[" "]" ";" tostr lst let string_of_args tostr lst = string_of_collection "(" ")" "," tostr lst From c6db16a028e40882b239b1ae1be9d9935119306a Mon Sep 17 00:00:00 2001 From: EL SIBAIE BESOGNET REMY <3361547@ari-41-307-06.infop6.jussieu.fr> Date: Tue, 15 Oct 2013 17:46:13 +0200 Subject: [PATCH 18/42] ajout dans le readme des consignes vues en cours --- src/Formula.ml | 14 ++++++++---- src/README | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/Formula.ml b/src/Formula.ml index 6c1976b..c4bf02a 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -19,12 +19,15 @@ let string_of_restr = function | Rany -> "." | Rpref acts -> string_of_collection_no_block "," string_of_preprefix acts -let to_string_of_existence = function Possibly -> sprintf "<%s>" +let string_of_existence = function Possibly -> sprintf "<%s>" | Necessity -> sprintf "[%s]" -let to_string_of_strongness f r = function Weak -> f (f r) + +let string_of_strongness f r = function Weak -> f (f r) | Strong -> f r + let string_of_modality (s, e, r) = - string_of_strongness (string_of_existence e) (string_of_restr r) + string_of_strongness (string_of_existence e) (string_of_restr r) s + type formula = | FTrue @@ -52,7 +55,7 @@ let rec string_of_formula : formula -> string = function | FMu(x,f) -> sprintf "Mu(%s).%s" x (string_of_formula f) | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) -type proposition = string * string list * formula +type proposition = Proposition of string * string list * formula let string_of_prop_header (name, params, _) = name ^ (string_of_args (fun x -> x) params) @@ -60,6 +63,9 @@ let string_of_prop_header (name, params, _) = let string_of_proposition ((_, _, formula) as prop) = "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) + + + let rec formula_of_preformula : formula -> formula = function | _ as f -> printf "Transforming %s\n" @@ string_of_formula f; diff --git a/src/README b/src/README index 99eb14e..bfae45d 100644 --- a/src/README +++ b/src/README @@ -27,3 +27,65 @@ Control.handle_global_check : TODO : + + + +CCS et µcalcul "pur" et "par valeur" : + +Vu en cours : + +µ-calcul "pur" : + + A |- P : Ok s'il existe P' tel que P-a!->P' et A |- P' + +<> A |- P + Ok si il existe un P' tq + - P -tau-> *P1 -a!-> P2 -tau -> *P' + - et A |- P' + + +µ-calcul par valeur : + +const %N=3 +type Range = [0..%N] + +def Incr = in ?($n:Range), + [when $n < %N out!($n+1), Incr + when $n = %N stop !,0] + + + +Ceci est transformé en CCS pur de la façon suivante : + + +def Incr = + +in_0 ?, [out_1 !,Incr + 0] + ++ in_1 ?, [out_2 !,Incr + 0] + ++ in_2 ?, [out_3 !,Incr + 0] + ++ in_3, [0 + stop !,0] + + +Une formule "par valeur" comme par exemple : + + +forall $n:Range, $n < %N ==> true + +devient une formule "pure" : + + +(true => True) + +and (true => True) + +and (true => True) + + + + +On ajoute : +Forall +Exists +conditions du type : \ No newline at end of file From 7ee1d6c5ee756a3817554b03393a4d5f7e869637 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Tue, 15 Oct 2013 22:55:33 +0200 Subject: [PATCH 19/42] force a commit --- src/Formula.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Formula.ml b/src/Formula.ml index 6db115a..79ae681 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -73,7 +73,7 @@ let rec string_of_formula : formula -> string = function type proposition = Proposition of string * string list * formula -let string_of_prop_header (Proposition(name, params, _)) = +let string_of_prop_header (Proposition(name, params, _)) = name ^ (string_of_args (fun x -> x) params) let string_of_proposition = function @@ -81,7 +81,7 @@ let string_of_proposition = function "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) let rec formula_of_preformula : formula -> formula = function - | _ as f -> + | _ as f -> printf "Transforming %s\n" @@ string_of_formula f; printf "Not implemented\n"; f From 9af3ac34032e097cea4c18a2e6236646d8c699b1 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Wed, 16 Oct 2013 01:00:10 +0200 Subject: [PATCH 20/42] simplification next_process : or_pattern --- src/Control.ml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 554cdbd..6b89e30 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -368,13 +368,9 @@ let next_process_set modality ts = let _, mod_to_check, destination = t in let is_next = match modality, mod_to_check with - | (Strong, Possibly, Rany), _ -> true - | (Strong, Possibly, Rout), (T_Out _) -> true - | (Strong, Possibly, Rin), (T_In _) -> true - - | (Strong, Necessity, Rany), _ -> true - | (Strong, Necessity, Rout), (T_Out _) -> true - | (Strong, Necessity, Rin), (T_In _) -> true + | ((Strong | Weak), (Possibly | Necessity), Rany), _ -> true + | ((Strong | Weak), (Possibly | Necessity), Rout), (T_Out _) -> true + | ((Strong | Weak), (Possibly | Necessity), Rin), (T_In _) -> true | (_, _, Rpref acts), label -> List.exists (check_label_prefixes label) acts From 1674f336269f787afa7eca0870b87410b49c1971 Mon Sep 17 00:00:00 2001 From: EL SIBAIE BESOGNET REMY <3361547@ari-31-207-07.ufr-info-p6.jussieu.fr> Date: Wed, 16 Oct 2013 11:30:32 +0200 Subject: [PATCH 21/42] simplification handleprop --- src/Control.ml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index 6b89e30..58029ce 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -368,9 +368,9 @@ let next_process_set modality ts = let _, mod_to_check, destination = t in let is_next = match modality, mod_to_check with - | ((Strong | Weak), (Possibly | Necessity), Rany), _ -> true - | ((Strong | Weak), (Possibly | Necessity), Rout), (T_Out _) -> true - | ((Strong | Weak), (Possibly | Necessity), Rin), (T_In _) -> true + | ((Strong | Weak), _, Rany), _ -> true + | ((Strong | Weak), _, Rout), (T_Out _) -> true + | ((Strong | Weak), _, Rin), (T_In _) -> true | (_, _, Rpref acts), label -> List.exists (check_label_prefixes label) acts @@ -381,8 +381,6 @@ let next_process_set modality ts = ) ts PSet.empty -let is_necessity = function _, Necessity, _ -> true | _ -> false - (* mod_to_check : @@ -419,12 +417,10 @@ let handle_check_local formula proc = | FImplies (f1, f2) -> check f1 p |> not || check f2 p | FModal (modality, formula) -> let ts = transitions_of p in - let necessity = is_necessity modality in - necessity && ts = TSet.empty - || (necessity && - PSet.for_all (check formula) @@ next_process_set modality ts) - || PSet.exists (check formula) @@ next_process_set modality ts - + let quantif = match modality with + | _, Necessity, _ -> PSet.for_all + | _, Possibly, _ -> PSet.exists in + quantif (check formula) (next_process_set modality ts) (* transitions : [a] *) (* | FInvModal (modality, formula) -> assert false (\* TODO *\) *) (* transitions : not ou not [a] *) From ab4db3da80ef3c22cadf3d7387e05b9c9fb6cca5 Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Wed, 16 Oct 2013 13:07:04 +0200 Subject: [PATCH 22/42] Debut basic de formula to preformula --- src/Formula.ml | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/Formula.ml b/src/Formula.ml index 61e8c8e..3e5f16f 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -79,21 +79,22 @@ let string_of_prop_header (name, params, _) = let string_of_proposition ((_, _, formula) as prop) = "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) -let rec formula_of_preformula : formula -> formula = function - | _ as f -> - printf "Transforming %s\n" @@ string_of_formula f; - printf "Not implemented\n"; - f - (* function - | FTrue - | FFalse - | FAnd (f, g) - | FOr (f, g) - | FImplies (f, g) - | FModal (m, f) - | FInvModal (m, f) - | FProp (prop, params) - | FVar var - | FMu (x, f) - | FNu (x, f) -> *) +let rec formula_of_preformula formula = + printf "Transforming %s\n" @@ string_of_formula formula; + match formula with + | FTrue -> formula + | FFalse -> formula + | FAnd (f1, f2) -> FAnd (formula_of_preformula f1, formula_of_preformula f2) + | FOr (f1, f2) -> FOr (formula_of_preformula f1, formula_of_preformula f2) + | FImplies (f1, f2) -> FImplies (formula_of_preformula f1, formula_of_preformula f2) + | FModal (m, f) -> FModal (m, formula_of_preformula f) + | FInvModal (m, f) -> FInvModal (m, formula_of_preformula f) + | FProp (prop, params) -> + printf "%s : Not implemented\n" @@ string_of_formula formula; + formula + | FVar var -> + printf "%s : Not implemented\n" @@ string_of_formula formula; + formula + | FMu (x, f) -> FMu (x, formula_of_preformula f) + | FNu (x, f) -> FNu (x, formula_of_preformula f) From f529fe5e6dceb97686fdbeac73ef56de233d7b8f Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Wed, 16 Oct 2013 20:23:48 +0200 Subject: [PATCH 23/42] Avancement check local --- src/Check.ml | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ src/Control.ml | 104 +++---------------------------------------- src/Formula.ml | 4 +- 3 files changed, 125 insertions(+), 100 deletions(-) create mode 100644 src/Check.ml diff --git a/src/Check.ml b/src/Check.ml new file mode 100644 index 0000000..9583ef9 --- /dev/null +++ b/src/Check.ml @@ -0,0 +1,117 @@ +open Formula +open Semop + +(* + +Dans checklocal : + - on normalise le proc + - une fois dans Fmodal : on appelle next_process_set et on + appelle check sur l'ensemble résultat +*) + +let transitions_of def_map nproc = Semop.derivatives def_map nproc + +(* Semop.PSet : set de processus *) + + +let check_label_prefixes lbl pref = + let open Presyntax in + match pref, lbl with + | PTau, T_Tau -> true + | (PIn (PName s1), (T_In s2)) when s1 = s2 -> true + | (POut (PName s1), (T_Out s2)) when s1 = s2 -> true + | _ -> false + + +(* recupère l'ensemble des processus suivants de nproc *) +let next_process_set modality transitions = + let choose transition destination_set = + let _, mod_to_check, destination = transition in + let is_next = + match modality, mod_to_check with + | ((Strong | Weak), _, Rany), _ -> true + | ((Strong | Weak), _, Rout), (T_Out _) -> true + | ((Strong | Weak), _, Rin), (T_In _) -> true + + | (_, _, Rpref acts), label -> + List.exists (check_label_prefixes label) acts + + | _ -> false + in + if is_next then PSet.add destination destination_set else destination_set + in + TSet.fold choose transitions PSet.empty + + +(* +mod_to_check : + +W: weak +Possibly : <> +Necessity : [] + +type preprefix = + | PTau + | PIn of preexpr + | POut of preexpr + | PReceive of preexpr * string * string + | PSend of preexpr * preexpr + +type label = T_Tau | T_In of name | T_Out of name + +============= + +type restr = Rin | Rout | Rany | Rpref of preprefix list +type existence = Possibly | Necessity +type strength = Weak | Strong +type modality = strength * existence * restr + +*) + +let rec check def_map prop_map formula nproc = + let rec check_internal formula = + match formula with + | FTrue -> true + | FFalse -> false + | FAnd (f1, f2) -> check_internal f1 && check_internal f2 + | FOr (f1, f2) -> check_internal f1 || check_internal f2 + | FImplies (f1, f2) -> check_internal f1 |> not || check_internal f2 + | FModal (modality, formula) -> + check_modality def_map prop_map modality formula nproc + (* transitions : [a] *) + | FInvModal (modality, formula) -> + not @@ check_modality def_map prop_map modality formula nproc + (* TODO : à vérifier la correctness *) + (* transitions : not ou not [a] *) + | _-> assert false + | FProp (prop, params) -> + let (Proposition (nom, expected_params, formula)) = + try + Hashtbl.find prop_map prop + with Not_found -> assert false (* TODO *) + in + let params_length1 = List.length params in + let params_length2 = List.length expected_params in + if params_length1 <> params_length2 then + assert false (* TODO *) + else + assert false + (* beta_reduce all params *) + (* | FVar var -> *) + (* (\* begin try let name, params, _ = fetch_prop prop in *\) *) + (* assert false (\* TODO *\) *) + (* (\* with Not_found -> raise @@ Error (Unbound_Proposition prop) *\) *) + (* (\* end *\) *) + (* | FMu (x, f) -> assert false (\* TODO *\) *) + (* | FNu (x, f) -> assert false (\* TODO *\) *) + in + check_internal formula + +and check_modality def_map prop_map modality formula process = + let ts = transitions_of def_map process in + let quantif = match modality with + | _, Necessity, _ -> PSet.for_all + | _, Possibly, _ -> PSet.exists + in + quantif (check def_map prop_map formula) (next_process_set modality ts) + diff --git a/src/Control.ml b/src/Control.ml index 58029ce..ff95537 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -335,108 +335,16 @@ let register_proposition prop = let handle_prop name params formula = if !script_mode then printf "> %s\n%!" (string_of_formula formula) ; - register_proposition @@ (name, params, formula); + register_proposition @@ Proposition(name, params, formula); printf "Proposition '%s' registered\n%!" name - -(* - -Dans checklocal : - - on normalise le proc - - une fois dans Fmodal : on appelle next_process_set et on - appelle check sur l'ensemble résultat -*) - -let transitions_of nproc = derivatives global_definition_map nproc - -(* Semop.PSet : set de processus *) - - -let check_label_prefixes lbl pref = - let open Presyntax in - match pref, lbl with - | PTau, T_Tau -> true - | (PIn (PName s1), (T_In s2)) when s1 = s2 -> true - | (POut (PName s1), (T_Out s2)) when s1 = s2 -> true - | _ -> false - - -(* recupère l'ensemble des processus suivants de nproc *) -let next_process_set modality ts = - TSet.fold (fun t set -> - let _, mod_to_check, destination = t in - let is_next = - match modality, mod_to_check with - | ((Strong | Weak), _, Rany), _ -> true - | ((Strong | Weak), _, Rout), (T_Out _) -> true - | ((Strong | Weak), _, Rin), (T_In _) -> true - - | (_, _, Rpref acts), label -> List.exists - (check_label_prefixes label) acts - - | _ -> false - in - if is_next then PSet.add destination set else set - ) ts PSet.empty - - -(* -mod_to_check : - -W: weak -Possibly : <> -Necessity : [] - -type preprefix = - | PTau - | PIn of preexpr - | POut of preexpr - | PReceive of preexpr * string * string - | PSend of preexpr * preexpr - -type label = T_Tau | T_In of name | T_Out of name - -============= - -type restr = Rin | Rout | Rany | Rpref of preprefix list -type existence = Possibly | Necessity -type strength = Weak | Strong -type modality = strength * existence * restr - -*) - -let handle_check_local formula proc = - let nproc = normalize proc in - let rec check formula p = - match formula with - | FTrue -> true - | FFalse -> false - | FAnd (f1, f2) -> check f1 p && check f2 p - | FOr (f1, f2) -> check f1 p || check f2 p - | FImplies (f1, f2) -> check f1 p |> not || check f2 p - | FModal (modality, formula) -> - let ts = transitions_of p in - let quantif = match modality with - | _, Necessity, _ -> PSet.for_all - | _, Possibly, _ -> PSet.exists in - quantif (check formula) (next_process_set modality ts) - (* transitions : [a] *) - (* | FInvModal (modality, formula) -> assert false (\* TODO *\) *) - (* transitions : not ou not [a] *) - | _-> assert false - (* | FProp (prop, params) -> assert false (\* TODO *\) *) - (* | FVar var -> *) - (* (\* begin try let name, params, _ = fetch_prop prop in *\) *) - (* assert false (\* TODO *\) *) - (* (\* with Not_found -> raise @@ Error (Unbound_Proposition prop) *\) *) - (* (\* end *\) *) - (* | FMu (x, f) -> assert false (\* TODO *\) *) - (* | FNu (x, f) -> assert false (\* TODO *\) *) +let handle_check_local formula process = + let nproc = Normalize.normalize process in + let res = + Check.check global_definition_map global_proposition_map formula nproc in - let res = check formula nproc in if res then printf "TRUE PROPERTY\n" else printf "FALSE PROPERTY\n" - -let handle_check_global f p = assert false (* TODO *) +let handle_check_global formula process = assert false (* TODO *) diff --git a/src/Formula.ml b/src/Formula.ml index 18b1d67..51491d4 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -57,10 +57,10 @@ let rec string_of_formula : formula -> string = function type proposition = Proposition of string * string list * formula -let string_of_prop_header (name, params, _) = +let string_of_prop_header (Proposition(name, params, _)) = name ^ (string_of_args (fun x -> x) params) -let string_of_proposition ((_, _, formula) as prop) = +let string_of_proposition (Proposition(_, _, formula) as prop) = "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) let rec formula_of_preformula formula = From 5c0ff6d7718f607a6cc0739fb58e6ed22a34f78d Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Wed, 16 Oct 2013 22:06:41 +0200 Subject: [PATCH 24/42] Implementation de prop call et de la beta_reduce --- src/Check.ml | 63 +++++++++++++++++++++++++++++++++++++------------- src/Formula.ml | 8 +++---- src/Parser.mly | 7 +++++- src/Pave.ml | 8 ------- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/Check.ml b/src/Check.ml index 9583ef9..d54ba70 100644 --- a/src/Check.ml +++ b/src/Check.ml @@ -1,6 +1,13 @@ open Formula open Semop + +type error = Unbound_Proposition of string +exception Error of error + +let print_error = function + | Unbound_Proposition s -> Printf.printf "unbound proposition %s" s + (* Dans checklocal : @@ -43,6 +50,22 @@ let next_process_set modality transitions = TSet.fold choose transitions PSet.empty +let beta_reduce in_formula expected_var replacement = + let rec beta_reduce = function + | FTrue | FFalse -> in_formula + | FAnd (f1, f2) -> FAnd(beta_reduce f1, beta_reduce f2) + | FOr (f1, f2) -> FOr(beta_reduce f1, beta_reduce f2) + | FImplies (f1, f2) -> FImplies(beta_reduce f1, beta_reduce f2) + | FModal (modality, formula) -> FModal(modality, beta_reduce formula) +(* transitions : [a] *) + | FInvModal (modality, formula) -> FInvModal(modality, beta_reduce formula) + | FProp (prop_name, params) -> in_formula + | FVar var when var = expected_var -> replacement + | FMu (x, formula) -> FMu(x, beta_reduce formula) + | FNu (x, formula) -> FNu(x, beta_reduce formula) + in + beta_reduce in_formula + (* mod_to_check : @@ -69,8 +92,9 @@ type modality = strength * existence * restr *) let rec check def_map prop_map formula nproc = - let rec check_internal formula = - match formula with + Printf.printf "Checking %s |- %s\n" (Normalize.string_of_nprocess nproc) + (string_of_formula formula); + let rec check_internal = function | FTrue -> true | FFalse -> false | FAnd (f1, f2) -> check_internal f1 && check_internal f2 @@ -83,21 +107,11 @@ let rec check def_map prop_map formula nproc = not @@ check_modality def_map prop_map modality formula nproc (* TODO : à vérifier la correctness *) (* transitions : not ou not [a] *) + | FProp (prop_name, params) -> + check_prop_call def_map prop_map prop_name params nproc + | FVar var -> + check_prop_call def_map prop_map var [] nproc | _-> assert false - | FProp (prop, params) -> - let (Proposition (nom, expected_params, formula)) = - try - Hashtbl.find prop_map prop - with Not_found -> assert false (* TODO *) - in - let params_length1 = List.length params in - let params_length2 = List.length expected_params in - if params_length1 <> params_length2 then - assert false (* TODO *) - else - assert false - (* beta_reduce all params *) - (* | FVar var -> *) (* (\* begin try let name, params, _ = fetch_prop prop in *\) *) (* assert false (\* TODO *\) *) (* (\* with Not_found -> raise @@ Error (Unbound_Proposition prop) *\) *) @@ -115,3 +129,20 @@ and check_modality def_map prop_map modality formula process = in quantif (check def_map prop_map formula) (next_process_set modality ts) +and check_prop_call def_map prop_map prop_name params process = + let (Proposition (nom, expected_params, formula)) = + try + Hashtbl.find prop_map prop_name + with Not_found -> raise @@ Error(Unbound_Proposition prop_name) + in + let params_length1 = List.length params in + let params_length2 = List.length expected_params in + if params_length1 <> params_length2 then + failwith "unmatched length" + else + let params_map = List.combine expected_params params in + let reduce_param formula (expected_param, param) = + beta_reduce formula expected_param param + in + let reduced_formula = List.fold_left reduce_param formula params_map in + check def_map prop_map reduced_formula process diff --git a/src/Formula.ml b/src/Formula.ml index 51491d4..3541983 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -37,7 +37,7 @@ type formula = | FImplies of formula * formula | FModal of modality * formula | FInvModal of modality * formula - | FProp of string * (string list) + | FProp of string * formula list | FVar of string | FMu of string * formula | FNu of string * formula @@ -50,7 +50,7 @@ let rec string_of_formula : formula -> string = function | FImplies(f,g) -> sprintf "(%s ==> %s)" (string_of_formula f) (string_of_formula g) | FModal(m,f) -> (string_of_modality m) ^ (string_of_formula f) | FInvModal(m,f) -> "~" ^ (string_of_modality m) ^ (string_of_formula f) - | FProp(prop,params) -> prop ^ (string_of_collection "(" ")" "," (fun s -> s) params) + | FProp(prop,params) -> prop ^ (string_of_collection "(" ")" "," string_of_formula params) | FVar(var) -> var | FMu(x,f) -> sprintf "Mu(%s).%s" x (string_of_formula f) | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) @@ -58,7 +58,7 @@ let rec string_of_formula : formula -> string = function type proposition = Proposition of string * string list * formula let string_of_prop_header (Proposition(name, params, _)) = - name ^ (string_of_args (fun x -> x) params) + name let string_of_proposition (Proposition(_, _, formula) as prop) = "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) @@ -75,7 +75,7 @@ let rec formula_of_preformula formula = | FInvModal (m, f) -> FInvModal (m, formula_of_preformula f) | FProp (prop, params) -> printf "%s : Not implemented\n" @@ string_of_formula formula; - formula + FProp(prop, List.map formula_of_preformula params) | FVar var -> printf "%s : Not implemented\n" @@ string_of_formula formula; formula diff --git a/src/Parser.mly b/src/Parser.mly index 3d885dd..0101aa7 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -361,6 +361,11 @@ | /* empty */ { [] } | expr list_of_exprs { $1::$2 } + + list_of_formulas: + | /* empty */ { [] } + | formula list_of_formulas { $1::$2 } + formula: | TRUE { FTrue } | FALSE { FFalse } @@ -371,7 +376,7 @@ | TILD modality formula { FInvModal($2,$3) } | MU LPAREN IDENT RPAREN DOT formula { FMu ($3,$6) } | NU LPAREN IDENT RPAREN DOT formula { FNu ($3,$6) } - | IDENT LPAREN list_of_names RPAREN { FProp($1,$3) } + | IDENT LPAREN list_of_formulas RPAREN { FProp($1,$3) } | IDENT { FVar($1) } modality: diff --git a/src/Pave.ml b/src/Pave.ml index e166315..50a7bfe 100644 --- a/src/Pave.ml +++ b/src/Pave.ml @@ -62,14 +62,6 @@ match !load_file with | Parsing.Parse_error -> parse_error_msg ~interactive_mode:true lexbuf - | Presyntax.Type_Exception msg -> - printf " ==> %s\n%!" msg - | Presyntax.Vardef_Exception name -> - printf " ==> Undefined var \"%s\"\n%!" name - | Presyntax.Typedef_Exception name -> - printf " ==> Undefined type \"%s\"\n%!" name - | Utils.Non_Implemented_Exception -> - printf " ==> Unimplemented action\n%!" | Control.Error e -> Control.print_error e done From c82639542d713cff9a9de789b66c19f51c053fcc Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Wed, 16 Oct 2013 22:49:35 +0200 Subject: [PATCH 25/42] Implementation de nu --- src/Check.ml | 24 +++++++++++++++--------- src/Formula.ml | 17 +++++++++++------ src/Parser.mly | 4 ++-- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/Check.ml b/src/Check.ml index 694253a..8a37e00 100644 --- a/src/Check.ml +++ b/src/Check.ml @@ -58,10 +58,11 @@ let beta_reduce in_formula expected_var replacement = | FModal (modality, formula) -> FModal(modality, beta_reduce formula) (* transitions : [a] *) | FInvModal (modality, formula) -> FInvModal(modality, beta_reduce formula) - | FProp (prop_name, params) -> in_formula + | FProp _ -> in_formula | FVar var when var = expected_var -> replacement - | FMu (x, formula) -> FMu(x, beta_reduce formula) - | FNu (x, formula) -> FNu(x, beta_reduce formula) + | FVar _ -> in_formula + | FMu (x, env, formula) -> FMu(x, env, beta_reduce formula) + | FNu (x, env, formula) -> FNu(x, env, beta_reduce formula) in beta_reduce in_formula @@ -91,9 +92,10 @@ type modality = strength * existence * restr *) let rec check def_map prop_map formula nproc = - Printf.printf "Checking %s |- %s\n" (Normalize.string_of_nprocess nproc) - (string_of_formula formula); - let rec check_internal = function + let rec check_internal formula = + Printf.printf "Checking %s |- %s\n" (Normalize.string_of_nprocess nproc) + (string_of_formula formula); + match formula with | FTrue -> true | FFalse -> false | FAnd (f1, f2) -> check_internal f1 && check_internal f2 @@ -109,13 +111,17 @@ let rec check def_map prop_map formula nproc = check_prop_call def_map prop_map prop_name params nproc | FVar var -> check_prop_call def_map prop_map var [] nproc - | _-> assert false (* (\* begin try let name, params, _ = fetch_prop prop in *\) *) (* assert false (\* TODO *\) *) (* (\* with Not_found -> raise @@ Error (Unbound_Proposition prop) *\) *) (* (\* end *\) *) - (* | FMu (x, f) -> assert false (\* TODO *\) *) - (* | FNu (x, f) -> assert false (\* TODO *\) *) + | FNu (x, env, formula) when List.mem nproc env -> true + | FNu (x, env, formula) -> + let reduced_formula = + beta_reduce formula x @@ FNu(x, nproc::env, formula) + in + check_internal reduced_formula + | FMu (x, env, f) -> assert false in check_internal formula diff --git a/src/Formula.ml b/src/Formula.ml index 138ba9a..4126814 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -2,6 +2,7 @@ open Printf +open Normalize open Presyntax open Utils @@ -39,8 +40,8 @@ type formula = | FInvModal of modality * formula | FProp of string * formula list | FVar of string - | FMu of string * formula - | FNu of string * formula + | FMu of string * nprocess list * formula + | FNu of string * nprocess list * formula let rec string_of_formula : formula -> string = function | FTrue -> "True" @@ -52,8 +53,12 @@ let rec string_of_formula : formula -> string = function | FInvModal(m,f) -> "~" ^ (string_of_modality m) ^ (string_of_formula f) | FProp(prop,params) -> prop ^ (string_of_collection "(" ")" "," string_of_formula params) | FVar(var) -> var - | FMu(x,f) -> sprintf "Mu(%s).%s" x (string_of_formula f) - | FNu(x,f) -> sprintf "Nu(%s).%s" x (string_of_formula f) + | FMu(x, env, f) -> + sprintf "Mu(%s){%s}.%s" x (string_of_args string_of_nprocess env) + (string_of_formula f) + | FNu(x, env, f) -> + sprintf "Nu(%s){%s}.%s" x (string_of_args string_of_nprocess env) + (string_of_formula f) type proposition = Proposition of string * string list * formula @@ -79,5 +84,5 @@ let rec formula_of_preformula formula = | FVar var -> printf "%s : Not implemented\n" @@ string_of_formula formula; formula - | FMu (x, f) -> FMu (x, formula_of_preformula f) - | FNu (x, f) -> FNu (x, formula_of_preformula f) + | FMu (x, env, f) -> FMu (x, env, formula_of_preformula f) + | FNu (x, env, f) -> FNu (x, env, formula_of_preformula f) diff --git a/src/Parser.mly b/src/Parser.mly index 0101aa7..b38e38d 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -374,8 +374,8 @@ | formula IMPLIES formula { FImplies ($1,$3) } | modality formula { FModal($1,$2) } | TILD modality formula { FInvModal($2,$3) } - | MU LPAREN IDENT RPAREN DOT formula { FMu ($3,$6) } - | NU LPAREN IDENT RPAREN DOT formula { FNu ($3,$6) } + | MU LPAREN IDENT RPAREN DOT formula { FMu ($3, [], $6) } + | NU LPAREN IDENT RPAREN DOT formula { FNu ($3, [], $6) } | IDENT LPAREN list_of_formulas RPAREN { FProp($1,$3) } | IDENT { FVar($1) } From 87a9eaa7f4cebdacc39c9a543aa41ca867f61e1f Mon Sep 17 00:00:00 2001 From: remyzorg Date: Wed, 16 Oct 2013 23:05:05 +0200 Subject: [PATCH 26/42] gestion du weak dans les modals --- src/Check.ml | 50 ++++++++++++++++++++++++-------------------------- src/Formula.ml | 4 ++-- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/Check.ml b/src/Check.ml index 9583ef9..b050cf1 100644 --- a/src/Check.ml +++ b/src/Check.ml @@ -23,24 +23,23 @@ let check_label_prefixes lbl pref = | _ -> false -(* recupère l'ensemble des processus suivants de nproc *) -let next_process_set modality transitions = +let rec next_process_set def_map modality transitions = let choose transition destination_set = let _, mod_to_check, destination = transition in - let is_next = - match modality, mod_to_check with - | ((Strong | Weak), _, Rany), _ -> true - | ((Strong | Weak), _, Rout), (T_Out _) -> true - | ((Strong | Weak), _, Rin), (T_In _) -> true - - | (_, _, Rpref acts), label -> - List.exists (check_label_prefixes label) acts - - | _ -> false - in - if is_next then PSet.add destination destination_set else destination_set - in - TSet.fold choose transitions PSet.empty + match modality, mod_to_check with + | ((Strong | Weak), _, Rany), _ + | ((Strong | Weak), _, Rout), (T_Out _) + | ((Strong | Weak), _, Rin), (T_In _) -> + PSet.add destination destination_set + | (Weak, _, _), T_Tau -> + PSet.union destination_set @@ + next_process_set def_map modality (transitions_of def_map destination) + | (_, _, Rpref acts), label -> + if List.exists (check_label_prefixes label) acts then + PSet.add destination destination_set + else destination_set + | _ -> destination_set + in TSet.fold choose transitions PSet.empty (* @@ -76,17 +75,15 @@ let rec check def_map prop_map formula nproc = | FAnd (f1, f2) -> check_internal f1 && check_internal f2 | FOr (f1, f2) -> check_internal f1 || check_internal f2 | FImplies (f1, f2) -> check_internal f1 |> not || check_internal f2 - | FModal (modality, formula) -> + | FModal (modality, formula) -> check_modality def_map prop_map modality formula nproc - (* transitions : [a] *) - | FInvModal (modality, formula) -> + | FInvModal (modality, formula) -> not @@ check_modality def_map prop_map modality formula nproc (* TODO : à vérifier la correctness *) - (* transitions : not ou not [a] *) | _-> assert false - | FProp (prop, params) -> + | FProp (prop, params) -> let (Proposition (nom, expected_params, formula)) = - try + try Hashtbl.find prop_map prop with Not_found -> assert false (* TODO *) in @@ -107,11 +104,12 @@ let rec check def_map prop_map formula nproc = in check_internal formula -and check_modality def_map prop_map modality formula process = +and check_modality def_map prop_map modality formula process = let ts = transitions_of def_map process in let quantif = match modality with - | _, Necessity, _ -> PSet.for_all - | _, Possibly, _ -> PSet.exists + | _, Necessity, _ -> PSet.for_all + | _, Possibly, _ -> PSet.exists in - quantif (check def_map prop_map formula) (next_process_set modality ts) + quantif (check def_map prop_map formula) + (next_process_set def_map modality ts) diff --git a/src/Formula.ml b/src/Formula.ml index 51491d4..94d3fea 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -73,10 +73,10 @@ let rec formula_of_preformula formula = | FImplies (f1, f2) -> FImplies (formula_of_preformula f1, formula_of_preformula f2) | FModal (m, f) -> FModal (m, formula_of_preformula f) | FInvModal (m, f) -> FInvModal (m, formula_of_preformula f) - | FProp (prop, params) -> + | FProp (_prop, _params) -> printf "%s : Not implemented\n" @@ string_of_formula formula; formula - | FVar var -> + | FVar _var -> printf "%s : Not implemented\n" @@ string_of_formula formula; formula | FMu (x, f) -> FMu (x, formula_of_preformula f) From bc55d4aa5f65013f5644551583bd66251f3ae9be Mon Sep 17 00:00:00 2001 From: remyzorg Date: Wed, 16 Oct 2013 23:39:11 +0200 Subject: [PATCH 27/42] mu --- src/Check.ml | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/src/Check.ml b/src/Check.ml index 694253a..13aed11 100644 --- a/src/Check.ml +++ b/src/Check.ml @@ -65,30 +65,6 @@ let beta_reduce in_formula expected_var replacement = in beta_reduce in_formula -(* -mod_to_check : - -W: weak -Possibly : <> -Necessity : [] - -type preprefix = - | PTau - | PIn of preexpr - | POut of preexpr - | PReceive of preexpr * string * string - | PSend of preexpr * preexpr - -type label = T_Tau | T_In of name | T_Out of name - -============= - -type restr = Rin | Rout | Rany | Rpref of preprefix list -type existence = Possibly | Necessity -type strength = Weak | Strong -type modality = strength * existence * restr - -*) let rec check def_map prop_map formula nproc = Printf.printf "Checking %s |- %s\n" (Normalize.string_of_nprocess nproc) @@ -109,16 +85,18 @@ let rec check def_map prop_map formula nproc = check_prop_call def_map prop_map prop_name params nproc | FVar var -> check_prop_call def_map prop_map var [] nproc - | _-> assert false - (* (\* begin try let name, params, _ = fetch_prop prop in *\) *) - (* assert false (\* TODO *\) *) - (* (\* with Not_found -> raise @@ Error (Unbound_Proposition prop) *\) *) - (* (\* end *\) *) - (* | FMu (x, f) -> assert false (\* TODO *\) *) + | FMu (x, formula) -> + let reduced_formula = beta_reduce formula x @@ (FMu (x, formula)) in + check_internal reduced_formula + (* | FNu (x, f) -> assert false (\* TODO *\) *) + | _-> assert false in check_internal formula + + (* AND rec *) + and check_modality def_map prop_map modality formula process = let ts = transitions_of def_map process in let quantif = match modality with @@ -128,6 +106,7 @@ and check_modality def_map prop_map modality formula process = quantif (check def_map prop_map formula) (next_process_set def_map modality ts) + and check_prop_call def_map prop_map prop_name params process = let (Proposition (nom, expected_params, formula)) = try From 8eb51bc3c84d28ef3160842ed4d9dac9a1ac49f7 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Thu, 17 Oct 2013 03:07:46 +0200 Subject: [PATCH 28/42] ajout global + une partie de bdd, renommage Check => Local --- src/Control.ml | 11 +++- src/Formula.ml | 8 ++- src/Global.ml | 118 +++++++++++++++++++++++++++++++++++++ src/{Check.ml => Local.ml} | 26 ++++---- src/Pave.ml | 8 ++- 5 files changed, 148 insertions(+), 23 deletions(-) create mode 100644 src/Global.ml rename src/{Check.ml => Local.ml} (93%) diff --git a/src/Control.ml b/src/Control.ml index ae83a74..a87d80e 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -29,6 +29,13 @@ Command summary:\n\ wlts -> show labelled transition system\n\ wmini -> minimize process\n\ wfbisim ? ~~ -> check weak bisimilarity (fast)\n\ + prop = | \n\ + prop ( , ) = \n\ + -> register a new propertie with/without params\n\ + checklocal |- \n\ + -> local model checking on the given proc\n\ + checkglobal |- \n\ + -> local model checking on the given proc\n\ ---\n\ help -> this help message\n\ quit -> quit the program\n\ @@ -342,9 +349,9 @@ let handle_prop name params formula = let handle_check_local formula process = let nproc = Normalize.normalize process in let res = - Check.check global_definition_map global_proposition_map formula nproc + Local.check global_definition_map global_proposition_map formula nproc in if res then printf "TRUE PROPERTY\n" else printf "FALSE PROPERTY\n" -let handle_check_global _formula _process = assert false (* TODO *) +let handle_check_global formula process = Global.check formula process diff --git a/src/Formula.ml b/src/Formula.ml index a38e9e4..64ca343 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -53,7 +53,8 @@ let rec string_of_formula : formula -> string = function | FModal(m,f) -> (string_of_modality m) ^ (string_of_formula f) | FInvModal(m,f) -> "~" ^ (string_of_modality m) ^ (string_of_formula f) | FProp(prop,params) -> prop ^ (string_of_collection "(" ")" "," string_of_formula params) - | FVar(var) -> var + | FVar var -> var + | FNot f -> sprintf "not %s" (string_of_formula f) | FMu(x, env, f) -> sprintf "Mu(%s){%s}.%s" x (string_of_args string_of_nprocess env) (string_of_formula f) @@ -63,7 +64,7 @@ let rec string_of_formula : formula -> string = function type proposition = Proposition of string * string list * formula -let string_of_prop_header (Proposition(name, params, _)) = +let string_of_prop_header (Proposition(name, _, _)) = name let string_of_proposition (Proposition(_, _, formula) as prop) = @@ -74,6 +75,7 @@ let rec formula_of_preformula formula = match formula with | FTrue -> formula | FFalse -> formula + | FNot _ -> formula | FAnd (f1, f2) -> FAnd (formula_of_preformula f1, formula_of_preformula f2) | FOr (f1, f2) -> FOr (formula_of_preformula f1, formula_of_preformula f2) | FImplies (f1, f2) -> FImplies (formula_of_preformula f1, formula_of_preformula f2) @@ -82,7 +84,7 @@ let rec formula_of_preformula formula = | FProp (prop, params) -> printf "%s : Not implemented\n" @@ string_of_formula formula; FProp(prop, List.map formula_of_preformula params) - | FVar var -> + | FVar _ -> printf "%s : Not implemented\n" @@ string_of_formula formula; formula | FMu (x, env, f) -> FMu (x, env, formula_of_preformula f) diff --git a/src/Global.ml b/src/Global.ml new file mode 100644 index 0000000..ee61aa8 --- /dev/null +++ b/src/Global.ml @@ -0,0 +1,118 @@ +(** Global Model Checking Module *) + + +type error = +| No_global_not + +exception Error of error + +let print_error = function + | No_global_not -> Printf.printf "Cannot global check formula with negation" + + +module Obdd = struct + type unique = int + + type t = Zero | One | Node of unique * int * t * t + type obdd = t + + module S = Set.Make( + struct + type t = int + let compare = compare + end + ) + type elt = S.t + + let zero = Zero + let empty = zero + let is_empty obdd = obdd == Zero + + let one = One + + let unique = function + | Zero -> 0 + | One -> 1 + | Node (u , _, _, _) -> u + + let hash_node i o1 o2 = (19 * (19 * i + unique o1) + unique o2) land max_int + + let unique_ref = ref 2 + + module HashedObdd = struct + type t = obdd + let hash = function + | Zero -> 0 + | One -> 1 + | Node (_, i, o1, o2) -> hash_node i o1 o2 + let equal k1 k2 = match k1, k2 with + | One, One + | Zero, Zero -> true + | Node (_, i1, l1, r1), Node (_, i2, l2, r2) -> + i1 = i2 && unique l1 = unique l2 && unique r1 = unique r2 + | _ -> false + end +end + +module Obddtbl = Hashtbl.Make (Obdd.HashedObdd) +let hsize = 19997 (* 200323 *) + + +open Obdd + + +(* let construct global_table i o1 o2 = *) +(* if o2 = Zero then o1 else *) +(* let obdd = Node (!unique_ref, i, o1, o2) in *) +(* try *) +(* Obddtbl.find global_table obdd *) +(* with Not_found -> *) +(* Obddtbl.add global_table obdd obdd; *) +(* incr unique_ref; *) +(* obdd *) + +(* let memo_rec2 f = *) +(* let h = Obddtbl.create hsize in *) +(* let rec g x = *) +(* try Obddtbl.find h x *) +(* with Not_found -> let y = f g x in Obddtbl.add h x y; y *) +(* in *) +(* g *) + + +(* let union = memo_rec2 ( *) +(* fun union -> function *) +(* | Node(_, i, o1, o2), One *) +(* | One, Node(_, i, o1, o2) -> construct i (union (o1, One)) o2 *) +(* | One, One -> One *) +(* | Zero, o *) +(* | o, Zero -> o *) +(* | (Node (_, i1, l1, r1) as o1), (Node (_, i2, l2, r2) as o2) -> *) +(* if i1 = i2 then *) +(* construct i1 (union (l1, l2)) (union (r1, r2)) *) +(* else if i1 > i2 then *) +(* construct i2 (union (o1, l2)) r2 *) +(* else (\* i1 < i2 *\) *) +(* construct i1 (union (l1, o2)) r1 *) +(* ) *) + +(* let union o1 o2 = union (o1, o2) *) + +let rec obdd_of_formula formula = + let open Formula in + match formula with + | FTrue -> One + | FFalse -> Zero + | FNot _ -> raise @@ Error (No_global_not) + (* | FAnd (f1, f2) -> Obdd.inter (obdd_of_formula f1) (obdd_of_formula f2) *) + (* | FOr (f1, f2) -> Obdd.union (obdd_of_formula f1) (obdd_of_formula f2) *) + (* | FImplies of formula * formula *) + (* | FModal of modality * formula *) + (* | FInvModal of modality * formula *) + (* | FProp of string * formula list *) + (* | FVar of string *) + (* | FMu of string * nprocess list * formula *) + (* | FNu of string * nprocess list * formula *) + | _ -> assert false + +let check _formula _proc = assert false (* TODO *) diff --git a/src/Check.ml b/src/Local.ml similarity index 93% rename from src/Check.ml rename to src/Local.ml index 36aa8fe..ed5938d 100644 --- a/src/Check.ml +++ b/src/Local.ml @@ -1,25 +1,22 @@ +(** Local Model Checking Module *) + open Formula open Semop -type error = Unbound_Proposition of string +type error = +| Unbound_Proposition of string +| Unmatching_length of string + exception Error of error let print_error = function - | Unbound_Proposition s -> Printf.printf "unbound proposition %s" s - -(* - -Dans checklocal : - - on normalise le proc - - une fois dans Fmodal : on appelle next_process_set et on - appelle check sur l'ensemble résultat -*) + | Unbound_Proposition s -> Printf.printf "unbound proposition %s\n" s + | Unmatching_length s -> + Printf.printf "unmatching length on proposition %s\n" s let transitions_of def_map nproc = Semop.derivatives def_map nproc -(* Semop.PSet : set de processus *) - let check_label_prefixes lbl pref = let open Presyntax in @@ -56,7 +53,6 @@ let beta_reduce in_formula expected_var replacement = | FOr (f1, f2) -> FOr(beta_reduce f1, beta_reduce f2) | FImplies (f1, f2) -> FImplies(beta_reduce f1, beta_reduce f2) | FModal (modality, formula) -> FModal(modality, beta_reduce formula) -(* transitions : [a] *) | FInvModal (modality, formula) -> FInvModal(modality, beta_reduce formula) | FProp _ -> in_formula | FVar var when var = expected_var -> replacement @@ -103,8 +99,6 @@ let rec check def_map prop_map formula nproc = check_internal formula - (* AND rec *) - and check_modality def_map prop_map modality formula process = let ts = transitions_of def_map process in let quantif = match modality with @@ -124,7 +118,7 @@ and check_prop_call def_map prop_map prop_name params process = let params_length1 = List.length params in let params_length2 = List.length expected_params in if params_length1 <> params_length2 then - failwith "unmatched length" + raise @@ Error (Unmatching_length prop_name) else let params_map = List.combine expected_params params in let reduce_param formula (expected_param, param) = diff --git a/src/Pave.ml b/src/Pave.ml index 50a7bfe..37949cd 100644 --- a/src/Pave.ml +++ b/src/Pave.ml @@ -61,8 +61,9 @@ match !load_file with printf " ==> %s\n%!" msg | Parsing.Parse_error -> parse_error_msg ~interactive_mode:true lexbuf - - | Control.Error e -> Control.print_error e + | Control.Error e -> Control.print_error e + | Local.Error e -> Local.print_error e + | Global.Error e -> Global.print_error e done | Some file -> @@ -80,6 +81,9 @@ match !load_file with printf " ==> %s\n%!" msg ; true | Parsing.Parse_error -> parse_error_msg lexbuf ; true + | Control.Error e -> Control.print_error e; true + | Local.Error e -> Local.print_error e; true + | Global.Error e -> Global.print_error e; true in if continue then loop (); From 978bbc623ed0ae879079bff07d413294666f9433 Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Thu, 17 Oct 2013 11:06:50 +0200 Subject: [PATCH 29/42] Correction bug dans beta-reduce --- src/Local.ml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Local.ml b/src/Local.ml index ed5938d..80de2d3 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -47,7 +47,8 @@ let rec next_process_set def_map modality transitions = let beta_reduce in_formula expected_var replacement = - let rec beta_reduce = function + let rec beta_reduce in_formula = + match in_formula with | FTrue | FFalse -> in_formula | FAnd (f1, f2) -> FAnd(beta_reduce f1, beta_reduce f2) | FOr (f1, f2) -> FOr(beta_reduce f1, beta_reduce f2) @@ -65,10 +66,7 @@ let beta_reduce in_formula expected_var replacement = let rec check def_map prop_map formula nproc = - let rec check_internal formula = - Printf.printf "Checking %s |- %s\n" (Normalize.string_of_nprocess nproc) - (string_of_formula formula); - match formula with + let rec check_internal = function | FTrue -> true | FNot formula -> not @@ check_internal formula | FFalse -> false @@ -85,9 +83,9 @@ let rec check def_map prop_map formula nproc = check_prop_call def_map prop_map prop_name params nproc | FVar var -> check_prop_call def_map prop_map var [] nproc - | FMu (x, env, formula) -> + | FMu (x, env, mu_formula) -> let formula' = - FNot (FNu (x, env, FNot (beta_reduce formula x (FNot (FVar x))))) + FNot (FNu (x, env, FNot (beta_reduce mu_formula x (FNot (FVar x))))) in check_internal formula' | FNu (_, env, _) when List.mem nproc env -> true | FNu (x, env, formula) -> @@ -110,19 +108,19 @@ and check_modality def_map prop_map modality formula process = and check_prop_call def_map prop_map prop_name params process = - let (Proposition (_, expected_params, formula)) = + let (Proposition (_, param_names, formula)) = try Hashtbl.find prop_map prop_name with Not_found -> raise @@ Error(Unbound_Proposition prop_name) in let params_length1 = List.length params in - let params_length2 = List.length expected_params in + let params_length2 = List.length param_names in if params_length1 <> params_length2 then raise @@ Error (Unmatching_length prop_name) else - let params_map = List.combine expected_params params in - let reduce_param formula (expected_param, param) = - beta_reduce formula expected_param param + let params_map = List.combine param_names params in + let reduce_param formula (param_name, param_content) = + beta_reduce formula param_name param_content in let reduced_formula = List.fold_left reduce_param formula params_map in check def_map prop_map reduced_formula process From 479a0b5f50c384f92ce31e49fc7e6450fb2b5e0f Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Thu, 17 Oct 2013 11:07:10 +0200 Subject: [PATCH 30/42] Ajouts exemples formula --- src/examples/formula.ccs | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/examples/formula.ccs diff --git a/src/examples/formula.ccs b/src/examples/formula.ccs new file mode 100644 index 0000000..3dd445b --- /dev/null +++ b/src/examples/formula.ccs @@ -0,0 +1,46 @@ +def D = a!, b?,(a! + b?); + +prop StartA = true; +prop NotStartA = false; +prop AllSecondB = [.]true; +prop ExistsSecondB = <.>true; +prop ExistsThirdA = <.>true; +prop EndsWithA = Mu(X).[.]false or [.]X; + +checklocal StartA |- D; +checklocal NotStartA |- D; +checklocal AllSecondB |- D; +checklocal ExistsSecondB |- D; +checklocal AllThirdA |- D; +checklocal ExistsThirdA |- D; +checklocal EndsWithA |- D; + +def P = a!,Q; +def Q = a!,P; + +prop AllA = Nu(X).X; + +checklocal AllA |- P; + +prop AllB = Nu(X).X; + +checklocal AllB |- P; + +prop Exercice5 = Mu(X).true or (<.>true and [.]X): +prop Possibly(A) = Mu(X).A or <.>X; +prop Deadlock = [.]false; +prop Always(A) = Nu(X).A and [.]X; +prop Continue = <.>true; +prop Eventualy(A) = Mu(X).A or ([.]X and <.> true); + +checklocal Always(Continue) |- P; +checklocal Possibly(Deadlock) |- P; +checklocal Possibly(Continue) |- D; +checklocal Always(Deadlock) |- D; + +def D2 = a!,(b? + D2); + +checklocal Always(Continue) |- D2; +checklocal Possibly(Continue) |- D2; +checklocal Possibly(Deadlock) |- D2; +checklocal Always(Deadlock) |- D2; From d8f2db426049f9f626237cf15f6aa2a5cdea2b3c Mon Sep 17 00:00:00 2001 From: remyzorg Date: Thu, 17 Oct 2013 23:21:00 +0200 Subject: [PATCH 31/42] =?UTF-8?q?obdd=20dans=20un=20fichier=20s=C3=A9par?= =?UTF-8?q?=C3=A9,=20global=20avanc=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Global.ml | 99 ++++----------------------------------------- src/Obdd.ml | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 92 deletions(-) create mode 100644 src/Obdd.ml diff --git a/src/Global.ml b/src/Global.ml index ee61aa8..e0491a2 100644 --- a/src/Global.ml +++ b/src/Global.ml @@ -10,107 +10,22 @@ let print_error = function | No_global_not -> Printf.printf "Cannot global check formula with negation" -module Obdd = struct - type unique = int - type t = Zero | One | Node of unique * int * t * t - type obdd = t - - module S = Set.Make( - struct - type t = int - let compare = compare - end - ) - type elt = S.t - - let zero = Zero - let empty = zero - let is_empty obdd = obdd == Zero - - let one = One - - let unique = function - | Zero -> 0 - | One -> 1 - | Node (u , _, _, _) -> u - - let hash_node i o1 o2 = (19 * (19 * i + unique o1) + unique o2) land max_int - - let unique_ref = ref 2 - - module HashedObdd = struct - type t = obdd - let hash = function - | Zero -> 0 - | One -> 1 - | Node (_, i, o1, o2) -> hash_node i o1 o2 - let equal k1 k2 = match k1, k2 with - | One, One - | Zero, Zero -> true - | Node (_, i1, l1, r1), Node (_, i2, l2, r2) -> - i1 = i2 && unique l1 = unique l2 && unique r1 = unique r2 - | _ -> false - end -end - -module Obddtbl = Hashtbl.Make (Obdd.HashedObdd) -let hsize = 19997 (* 200323 *) - - -open Obdd - - -(* let construct global_table i o1 o2 = *) -(* if o2 = Zero then o1 else *) -(* let obdd = Node (!unique_ref, i, o1, o2) in *) -(* try *) -(* Obddtbl.find global_table obdd *) -(* with Not_found -> *) -(* Obddtbl.add global_table obdd obdd; *) -(* incr unique_ref; *) -(* obdd *) - -(* let memo_rec2 f = *) -(* let h = Obddtbl.create hsize in *) -(* let rec g x = *) -(* try Obddtbl.find h x *) -(* with Not_found -> let y = f g x in Obddtbl.add h x y; y *) -(* in *) -(* g *) - - -(* let union = memo_rec2 ( *) -(* fun union -> function *) -(* | Node(_, i, o1, o2), One *) -(* | One, Node(_, i, o1, o2) -> construct i (union (o1, One)) o2 *) -(* | One, One -> One *) -(* | Zero, o *) -(* | o, Zero -> o *) -(* | (Node (_, i1, l1, r1) as o1), (Node (_, i2, l2, r2) as o2) -> *) -(* if i1 = i2 then *) -(* construct i1 (union (l1, l2)) (union (r1, r2)) *) -(* else if i1 > i2 then *) -(* construct i2 (union (o1, l2)) r2 *) -(* else (\* i1 < i2 *\) *) -(* construct i1 (union (l1, o2)) r1 *) -(* ) *) - -(* let union o1 o2 = union (o1, o2) *) - -let rec obdd_of_formula formula = +let rec obdd_of_formula env formula = let open Formula in + let open Obdd in match formula with | FTrue -> One | FFalse -> Zero | FNot _ -> raise @@ Error (No_global_not) - (* | FAnd (f1, f2) -> Obdd.inter (obdd_of_formula f1) (obdd_of_formula f2) *) - (* | FOr (f1, f2) -> Obdd.union (obdd_of_formula f1) (obdd_of_formula f2) *) + | FAnd (f1, f2) -> Obdd.inter (obdd_of_formula env f1) (obdd_of_formula env f2) + | FOr (f1, f2) -> Obdd.union (obdd_of_formula env f1) + (obdd_of_formula env f2) (* | FImplies of formula * formula *) - (* | FModal of modality * formula *) + | FModal (modality, formula) -> assert false (* | FInvModal of modality * formula *) (* | FProp of string * formula list *) - (* | FVar of string *) + | FVar v -> assert false (* | FMu of string * nprocess list * formula *) (* | FNu of string * nprocess list * formula *) | _ -> assert false diff --git a/src/Obdd.ml b/src/Obdd.ml new file mode 100644 index 0000000..f9c9015 --- /dev/null +++ b/src/Obdd.ml @@ -0,0 +1,110 @@ + +type unique = int + +type t = Zero | One | Node of unique * int * t * t +type obdd = t + +module S = Set.Make( + struct + type t = int + let compare = compare + end +) +type elt = S.t + +let zero = Zero +let empty = zero +let is_empty obdd = obdd == Zero +let one = One + +let unique = function + | Zero -> 0 + | One -> 1 + | Node (u , _, _, _) -> u + +let hash_node i o1 o2 = (19 * (19 * i + unique o1) + unique o2) land max_int + +let unique_ref = ref 2 + +module HashedObdd = struct + type t = obdd + let hash = function + | Zero -> 0 + | One -> 1 + | Node (_, i, o1, o2) -> hash_node i o1 o2 + let equal k1 k2 = match k1, k2 with + | One, One + | Zero, Zero -> true + | Node (_, i1, l1, r1), Node (_, i2, l2, r2) -> + i1 = i2 && unique l1 = unique l2 && unique r1 = unique r2 + | _ -> false +end + +module Obddtbl = Hashtbl.Make (HashedObdd) + +let hsize = 19997 (* 200323 *) + + +module H2 = Hashtbl.Make + (struct + type t = obdd * obdd + let hash (o1, o2) = (19 * unique o1 + unique o2) land max_int + let equal (o11, o12) (o21, o22) = o11 == o21 && o12 == o22 + end) + +let global_table = Obddtbl.create hsize + +let construct i o1 o2 = + if o2 = Zero then o1 else + let obdd = Node (!unique_ref, i, o1, o2) in + try + Obddtbl.find global_table obdd + with Not_found -> + Obddtbl.add global_table obdd obdd; + incr unique_ref; + obdd + +let memo_rec2 f = + let h = H2.create hsize in + let rec g x = + try H2.find h x + with Not_found -> let y = f g x in H2.add h x y; y + in + g + +let union = memo_rec2 ( + fun union -> function + | Node(_, i, o1, o2), One + | One, Node(_, i, o1, o2) -> construct i (union (o1, One)) o2 + | One, One -> One + | Zero, o | o, Zero -> o + | (Node (_, i1, l1, r1) as o1), (Node (_, i2, l2, r2) as o2) -> + if i1 = i2 then + construct i1 (union (l1, l2)) (union (r1, r2)) + else if i1 > i2 then + construct i2 (union (o1, l2)) r2 + else (* i1 < i2 *) + construct i1 (union (l1, o2)) r1 +) + +let union o1 o2 = union (o1, o2) + +let inter = memo_rec2 ( + fun inter -> function + | Node(_, _, o1, _), One + | One, Node(_, _, o1, _) -> inter (o1, One) + | One, One -> One + | Zero, _ + | _, Zero -> Zero + | (Node (_, i1, l1, r1) as o1), + (Node (_, i2, l2, r2) as o2) -> + if i1 = i2 then + construct i1 (inter (l1, l2)) (inter (r1, r2)) + else if i1 > i2 then + inter (o1, l2) + else + inter (o2, l1) + ) + +let inter o1 o2 = + inter (o1, o2) From 5d7e944e635089d856b76d321c05df18bc104602 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Fri, 18 Oct 2013 18:48:18 +0200 Subject: [PATCH 32/42] global sur les modal : en cours --- src/Global.ml | 24 +++++++++++++++++++----- src/Local.ml | 2 +- src/Obdd.ml | 10 +++++----- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/Global.ml b/src/Global.ml index e0491a2..b88f381 100644 --- a/src/Global.ml +++ b/src/Global.ml @@ -11,6 +11,18 @@ let print_error = function + +let rec obdd_of_modality env modality = + match modality with + (* | Strong, Possibly, Rany _ *) + (* | Strong, Possibly, Rout *) + (* | Strong, Possibly, Rin -> *) + (* | (Weak, _, _), T_Tau -> *) + (* | (_, _, Rpref acts), label -> *) + | _ -> assert false + + + let rec obdd_of_formula env formula = let open Formula in let open Obdd in @@ -18,16 +30,18 @@ let rec obdd_of_formula env formula = | FTrue -> One | FFalse -> Zero | FNot _ -> raise @@ Error (No_global_not) - | FAnd (f1, f2) -> Obdd.inter (obdd_of_formula env f1) (obdd_of_formula env f2) - | FOr (f1, f2) -> Obdd.union (obdd_of_formula env f1) + | FAnd (f1, f2) -> inter (obdd_of_formula env f1) + (obdd_of_formula env f2) + | FOr (f1, f2) -> union (obdd_of_formula env f1) (obdd_of_formula env f2) (* | FImplies of formula * formula *) - | FModal (modality, formula) -> assert false + | FModal (modality, f) -> + inter (obdd_of_formula env f) (obdd_of_modality env f) (* | FInvModal of modality * formula *) (* | FProp of string * formula list *) - | FVar v -> assert false + (* | FVar v -> assert false *) (* | FMu of string * nprocess list * formula *) (* | FNu of string * nprocess list * formula *) - | _ -> assert false + | _ -> assert false let check _formula _proc = assert false (* TODO *) diff --git a/src/Local.ml b/src/Local.ml index 80de2d3..08077a3 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -47,7 +47,7 @@ let rec next_process_set def_map modality transitions = let beta_reduce in_formula expected_var replacement = - let rec beta_reduce in_formula = + let rec beta_reduce in_formula = match in_formula with | FTrue | FFalse -> in_formula | FAnd (f1, f2) -> FAnd(beta_reduce f1, beta_reduce f2) diff --git a/src/Obdd.ml b/src/Obdd.ml index f9c9015..f3e4ff5 100644 --- a/src/Obdd.ml +++ b/src/Obdd.ml @@ -52,9 +52,10 @@ module H2 = Hashtbl.Make let equal (o11, o12) (o21, o22) = o11 == o21 && o12 == o22 end) -let global_table = Obddtbl.create hsize -let construct i o1 o2 = +let construct = + let global_table = Obddtbl.create hsize in + fun i o1 o2 -> if o2 = Zero then o1 else let obdd = Node (!unique_ref, i, o1, o2) in try @@ -83,7 +84,7 @@ let union = memo_rec2 ( construct i1 (union (l1, l2)) (union (r1, r2)) else if i1 > i2 then construct i2 (union (o1, l2)) r2 - else (* i1 < i2 *) + else construct i1 (union (l1, o2)) r1 ) @@ -106,5 +107,4 @@ let inter = memo_rec2 ( inter (o2, l1) ) -let inter o1 o2 = - inter (o1, o2) +let inter o1 o2 = inter (o1, o2) From 03c43d469fa8278dc40605511d6525ac3e8e6f06 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Sun, 17 Nov 2013 23:33:56 +0100 Subject: [PATCH 33/42] faire emersonlei --- src/Global.ml | 3 +++ src/Local.ml | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Global.ml b/src/Global.ml index b88f381..823d7e0 100644 --- a/src/Global.ml +++ b/src/Global.ml @@ -12,6 +12,9 @@ let print_error = function +(* let rec emerson_lei formula *) + + let rec obdd_of_modality env modality = match modality with (* | Strong, Possibly, Rany _ *) diff --git a/src/Local.ml b/src/Local.ml index 08077a3..ad9785a 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -16,6 +16,7 @@ let print_error = function Printf.printf "unmatching length on proposition %s\n" s let transitions_of def_map nproc = Semop.derivatives def_map nproc +let weak_transitions_of def_map nproc = Semop.weak_derivatives def_map nproc let check_label_prefixes lbl pref = From 8d7d5621b0aec14af6e68143f35c6a8f638d0fb6 Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Sat, 23 Nov 2013 13:28:44 +0100 Subject: [PATCH 34/42] Ajout de traces --- src/Control.ml | 18 +++++++++--- src/Formula.ml | 6 ++-- src/Local.ml | 77 +++++++++++++++++++++++++++++++++----------------- 3 files changed, 68 insertions(+), 33 deletions(-) diff --git a/src/Control.ml b/src/Control.ml index a87d80e..0f890aa 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -348,10 +348,20 @@ let handle_prop name params formula = let handle_check_local formula process = let nproc = Normalize.normalize process in - let res = - Local.check global_definition_map global_proposition_map formula nproc + let res, trace = + Local.check global_definition_map global_proposition_map + [formula, nproc] formula nproc in - if res then printf "TRUE PROPERTY\n" - else printf "FALSE PROPERTY\n" + if res then printf "TRUE PROPERTY\n\n" + else begin + printf "Trace : \n"; + List.iter + (fun (formula, process) -> + printf "\t%s -| %s\n" + (Normalize.string_of_nprocess process) + (Formula.string_of_formula formula)) + (List.rev trace); + printf "FALSE PROPERTY\n\n" + end let handle_check_global formula process = Global.check formula process diff --git a/src/Formula.ml b/src/Formula.ml index 64ca343..f58ceb5 100644 --- a/src/Formula.ml +++ b/src/Formula.ml @@ -71,7 +71,7 @@ let string_of_proposition (Proposition(_, _, formula) as prop) = "prop " ^ (string_of_prop_header prop) ^ " = " ^ (string_of_formula formula) let rec formula_of_preformula formula = - printf "Transforming %s\n" @@ string_of_formula formula; + (* printf "Transforming %s\n" @@ string_of_formula formula; *) match formula with | FTrue -> formula | FFalse -> formula @@ -82,10 +82,10 @@ let rec formula_of_preformula formula = | FModal (m, f) -> FModal (m, formula_of_preformula f) | FInvModal (m, f) -> FInvModal (m, formula_of_preformula f) | FProp (prop, params) -> - printf "%s : Not implemented\n" @@ string_of_formula formula; + (* printf "%s : Not implemented\n" @@ string_of_formula formula; *) FProp(prop, List.map formula_of_preformula params) | FVar _ -> - printf "%s : Not implemented\n" @@ string_of_formula formula; + (* printf "%s : Not implemented\n" @@ string_of_formula formula; *) formula | FMu (x, env, f) -> FMu (x, env, formula_of_preformula f) | FNu (x, env, f) -> FNu (x, env, formula_of_preformula f) diff --git a/src/Local.ml b/src/Local.ml index 80de2d3..4408246 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -3,13 +3,16 @@ open Formula open Semop - type error = | Unbound_Proposition of string | Unmatching_length of string exception Error of error +(* Trace, tuple of labels visited and + process remaining to execute *) +type trace = (formula * Normalize.nprocess) list + let print_error = function | Unbound_Proposition s -> Printf.printf "unbound proposition %s\n" s | Unmatching_length s -> @@ -64,50 +67,71 @@ let beta_reduce in_formula expected_var replacement = in beta_reduce in_formula - -let rec check def_map prop_map formula nproc = - let rec check_internal = function - | FTrue -> true - | FNot formula -> not @@ check_internal formula - | FFalse -> false - | FAnd (f1, f2) -> check_internal f1 && check_internal f2 - | FOr (f1, f2) -> check_internal f1 || check_internal f2 - | FImplies (f1, f2) -> check_internal f1 |> not || check_internal f2 +let rec check def_map prop_map trace formula nproc = + let rec check_internal trace = function + | FTrue -> (true, trace) + | FNot formula -> + let okay1, trace1 = check_internal (( formula, nproc)::trace) formula in + (not okay1, trace1) + | FFalse -> (false, trace) + | FAnd (f1, f2) -> + let okay1, trace1 = check_internal (( f1, nproc)::trace) f1 in + if not okay1 then okay1, trace1 else + let okay2, trace2 = check_internal (( f2, nproc)::trace1) f2 in + (okay1 && okay2, trace2) + | FOr (f1, f2) -> + let okay1, trace1 = check_internal (( f1, nproc)::trace) f1 in + if okay1 then okay1, trace else + let okay2, trace2 = check_internal (( f2, nproc)::trace1) f2 in + (okay1 || okay2, trace2) + | FImplies (f1, f2) -> + let okay1, trace1 = check_internal (( f1, nproc)::trace) f1 in + if not okay1 then not okay1, trace1 else + let okay2, trace2 = check_internal (( f2, nproc)::trace1) f2 in + (not okay1 || okay2, trace2) | FModal (modality, formula) -> - check_modality def_map prop_map modality formula nproc + check_modality def_map prop_map trace modality formula nproc | FInvModal (modality, formula) -> - not @@ check_modality def_map prop_map modality formula nproc + let okay1, trace1 = + check_modality def_map prop_map trace modality formula nproc + in + (not okay1, trace1) (* TODO : à vérifier la correctness *) (* transitions : not ou not [a] *) | FProp (prop_name, params) -> - check_prop_call def_map prop_map prop_name params nproc + check_prop_call def_map prop_map prop_name trace formula params nproc | FVar var -> - check_prop_call def_map prop_map var [] nproc + check_prop_call def_map prop_map var trace formula [] nproc | FMu (x, env, mu_formula) -> let formula' = FNot (FNu (x, env, FNot (beta_reduce mu_formula x (FNot (FVar x))))) - in check_internal formula' - | FNu (_, env, _) when List.mem nproc env -> true + in check_internal (( formula', nproc)::trace) formula' + | FNu (_, env, _) when List.mem nproc env -> (true, trace) | FNu (x, env, formula) -> let reduced_formula = beta_reduce formula x @@ FNu(x, nproc::env, formula) in - check_internal reduced_formula + check_internal (( reduced_formula, nproc)::trace) reduced_formula in - check_internal formula + check_internal trace formula -and check_modality def_map prop_map modality formula process = +and check_modality def_map prop_map trace modality formula process = let ts = transitions_of def_map process in - let quantif = match modality with - | _, Necessity, _ -> PSet.for_all - | _, Possibly, _ -> PSet.exists + let operator = match modality with + | _, Necessity, _ -> (&&) + | _, Possibly, _ -> (||) + in + let folding element (acc_okay, acc_trace) = + let okay1, trace1 = + check def_map prop_map ((formula, process)::acc_trace) formula element + in + (operator okay1 acc_okay, trace1) in - quantif (check def_map prop_map formula) - (next_process_set def_map modality ts) + PSet.fold folding (next_process_set def_map modality ts) (false, trace) -and check_prop_call def_map prop_map prop_name params process = +and check_prop_call def_map prop_map prop_name trace formula params process = let (Proposition (_, param_names, formula)) = try Hashtbl.find prop_map prop_name @@ -123,4 +147,5 @@ and check_prop_call def_map prop_map prop_name params process = beta_reduce formula param_name param_content in let reduced_formula = List.fold_left reduce_param formula params_map in - check def_map prop_map reduced_formula process + check def_map prop_map ((reduced_formula, process)::trace) + reduced_formula process From dae0f1f88a8583ad8951eeec74562444843efe0a Mon Sep 17 00:00:00 2001 From: remyzorg Date: Sat, 23 Nov 2013 18:20:01 +0100 Subject: [PATCH 35/42] weak transitions --- src/Local.ml | 7 +++++-- src/Normalize.ml | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/Local.ml b/src/Local.ml index ad9785a..dd6c045 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -16,7 +16,7 @@ let print_error = function Printf.printf "unmatching length on proposition %s\n" s let transitions_of def_map nproc = Semop.derivatives def_map nproc -let weak_transitions_of def_map nproc = Semop.weak_derivatives def_map nproc +let weak_transitions_of def_map nproc = Semop.weak_derivatives true def_map nproc let check_label_prefixes lbl pref = @@ -28,6 +28,7 @@ let check_label_prefixes lbl pref = | _ -> false + let rec next_process_set def_map modality transitions = let choose transition destination_set = let _, mod_to_check, destination = transition in @@ -38,7 +39,9 @@ let rec next_process_set def_map modality transitions = PSet.add destination destination_set | (Weak, _, _), T_Tau -> PSet.union destination_set @@ - next_process_set def_map modality (transitions_of def_map destination) + (PrefixMap.fold (fun k d a -> PSet.union d a) + (weak_transitions_of def_map destination) + destination_set) | (_, _, Rpref acts), label -> if List.exists (check_label_prefixes label) acts then PSet.add destination destination_set diff --git a/src/Normalize.ml b/src/Normalize.ml index c351aa9..c0bf02c 100644 --- a/src/Normalize.ml +++ b/src/Normalize.ml @@ -38,7 +38,7 @@ let string_of_nprocess (res, nproc) = else "new" ^ (string_of_args (fun x -> x) (SSet.elements res)) ^ "[" ^ (string_of_nproc nproc) ^ "]" - + let is_normalized (_, nproc,_) = let rec norm = function | NPrefix (_,q) -> norm q @@ -102,16 +102,16 @@ let denormalize (res, nproc) = | NRename(var,name,p) -> Rename (var,name,denorm_sub p) in SSet.fold (fun n p -> Res (n, p)) res (denorm_sub nproc) - + (***) -let rec mem_target a list = +let rec mem_target a list = match list with | [] -> false | (target,_)::tl -> if (target = a) then true else mem_target a tl -let mem_value a list = +let mem_value a list = match list with - | [] -> false + | [] -> false | (_,value)::tl -> if (value = a) then true else mem_target a tl @@ -130,13 +130,13 @@ let simple_normalize proc = let rec snorm_one map = function | Silent -> Silent | Prefix (Tau, proc) -> Prefix (Tau, snorm_one map proc) - | Prefix (In name, proc) -> + | Prefix (In name, proc) -> Prefix (In (SMap.find name map), snorm_one map proc) | Prefix (Out name, proc) -> Prefix (Out (SMap.find name map), snorm_one map proc) | Sum (proc1, proc2) -> Sum(snorm_one map proc1, snorm_one map proc2) | Par (proc1, proc2) -> Par(snorm_one map proc1, snorm_one map proc2) - | Res (name, proc) -> + | Res (name, proc) -> let fname = gen() in let map' = SMap.add name fname map @@ -153,16 +153,16 @@ let simple_normalize proc = Rename(old,value', snorm_one map proc) in let tmpproc = - snorm_one init_map proc + snorm_one init_map proc in let findname name map = - if SSet.mem name !nus - then + if SSet.mem name !nus + then name else SMap.find name map in - let rec norm_sub map = function + let rec norm_sub map = function | Silent -> NSilent | Prefix (Tau, proc) -> NPrefix (Tau, norm_sub map proc) | Prefix (In name, proc) -> NPrefix (In (findname name map), norm_sub map proc) @@ -184,7 +184,7 @@ let simple_normalize proc = | Res (_, proc) -> norm_sub map proc | Call (name, args) -> NCall (name, args) | Rename (old,value,proc) -> - let value' = + let value' = if SMap.mem value map then findname value map else value @@ -259,8 +259,8 @@ let complex_normalize ((bounded : SSet.t), nproc) frees = NPrefix (Out (SMap.find name name_map), rename np) | NSum nps -> NSum (List.map rename nps) | NPar nps -> NPar (List.map rename nps) - | NRename(var,name,np) -> - let name' = + | NRename(var,name,np) -> + let name' = if SMap.mem name name_map then SMap.find name name_map else name @@ -303,7 +303,7 @@ let nproc_subst nproc m n = let nsubst (res, nproc) m n = (SSet.add n (SSet.remove m res), nproc_subst nproc m n) - + let rec nsubsts p ms ns = match (ms,ns) with | ([],[]) -> p | ([],_) -> failwith "nsubsts: bad cosupport" From dd5a8a4c014010df2e5cc0110beae6c3689b707f Mon Sep 17 00:00:00 2001 From: remyzorg Date: Sun, 24 Nov 2013 19:30:17 +0100 Subject: [PATCH 36/42] rapport --- CMU-CS-96-180.ps | Bin 0 -> 248757 bytes rapport/.gitignore | 4 ++++ rapport/rapport.tex | 54 ++++++++++++++++++++++++++++++++++++++++++++ src/Global.ml | 17 +++++++++++++- src/Minim.ml | 16 ++++++------- src/Obdd.ml | 1 + 6 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 CMU-CS-96-180.ps create mode 100644 rapport/.gitignore create mode 100644 rapport/rapport.tex diff --git a/CMU-CS-96-180.ps b/CMU-CS-96-180.ps new file mode 100644 index 0000000000000000000000000000000000000000..4a830eefdee70afa670f301074c8a399353197b4 GIT binary patch literal 248757 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͑!43(U#HyR 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 a?>-|G_v`Xw`S1TMt_FYk%k#f|Irv|j7TKWy literal 0 HcmV?d00001 diff --git a/rapport/.gitignore b/rapport/.gitignore new file mode 100644 index 0000000..53373f9 --- /dev/null +++ b/rapport/.gitignore @@ -0,0 +1,4 @@ +*.log +*~ +*.aux +*.pdf \ No newline at end of file diff --git a/rapport/rapport.tex b/rapport/rapport.tex new file mode 100644 index 0000000..7bf2c86 --- /dev/null +++ b/rapport/rapport.tex @@ -0,0 +1,54 @@ + + +\pdfminorversion 7 +\pdfobjcompresslevel 3 + +\documentclass[a4paper]{article} +\special{papersize=210mm,297mm} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{cite} +\usepackage[francais]{babel} +\usepackage[bookmarks=false,colorlinks,linkcolor=blue]{hyperref} +\usepackage[top=3cm,bottom=2cm,left=3cm,right=2cm]{geometry} +\usepackage{graphicx} +\usepackage{subfig} +\usepackage{eso-pic} +\usepackage{array} +\usepackage{color} +\usepackage{url} +\usepackage{listings} +\usepackage{eurosym} +\usepackage{url} +\usepackage{textcomp} +\usepackage{fancyhdr} +\usepackage{tikz} +\usetikzlibrary{automata,positioning} + +\definecolor{lightgray}{gray}{0.9} + +\title{Projet de SACC} +\author{Rémy \textsc{El-Sibaie Besognet} -- Roven \textsc{Gabriel}} + +\newcommand{\HRule}{\rule{\linewidth}{0.5mm}} + + +\begin{document} + +\maketitle + +\section{Introduction} + +\section{$\mu$-Calcul par valeur} + +\section{Model checking local} + +\section{Model checking global} + +\section{Exemples} + +\end{document} + +# Local Variables: +# compile-command: "rubber -d rapport.tex" +# End: diff --git a/src/Global.ml b/src/Global.ml index 823d7e0..eed1801 100644 --- a/src/Global.ml +++ b/src/Global.ml @@ -12,8 +12,23 @@ let print_error = function -(* let rec emerson_lei formula *) +(* + L p = sous ensemble de Top tq p est vraie + T action = relation binaire sur t en fonction de a +*) +let rec simple_eval process defs formula env = + let lts = Semop.lts defs Semop.derivatives process in + let rec eval formula env = + let open Formula in + match formula with + | FNot _ -> raise @@ Error (No_global_not) + | FTrue -> lts + | FFalse -> [] + | FAnd (f1, f2) -> eval f1 env @ eval f2 env + | FOr (f1, f2) -> eval f1 env @ eval f2 env + | _ -> assert false + in eval formula env let rec obdd_of_modality env modality = match modality with diff --git a/src/Minim.ml b/src/Minim.ml index a272f87..98f3fa3 100755 --- a/src/Minim.ml +++ b/src/Minim.ml @@ -53,7 +53,7 @@ let string_of_graph g = in GMap.fold folder g "" -let string_of_partition parts = +let string_of_partition parts = (List.fold_left (fun s part -> s ^ "\n" ^ (string_of_gset string_of_gstate part)) "" parts) ^ "\n" @@ -110,12 +110,12 @@ let build_graph f_deriv init_graph init_partition defs ps = | _ -> (init_graph, init_partition) let rec refine (graph, part) = - let prevs = + let prevs = List.map (fun x -> GSet.fold (fun x' acc -> GSet.union acc (GMap.find x' graph)) x GSet.empty) part in - let split part prev = + let split part prev = let p1 = GSet.inter part prev in let p2 = GSet.diff part prev in (p1, p2) @@ -124,7 +124,7 @@ let rec refine (graph, part) = match pt with | [] -> [] | h1::t1 -> (f2 h1 pr)@(f1 t1 pr) - and f2 pt pr = + and f2 pt pr = match pr with | [] -> [pt] | h2::t2 -> let (spl1, spl2) = split pt h2 in @@ -138,7 +138,7 @@ let rec refine (graph, part) = then part' else refine (graph, part') let build_lts partition = - let (ps, ls) = + let (ps, ls) = List.partition (fun x -> match GSet.choose x with | LState _ -> false | PState _ -> true) @@ -164,15 +164,15 @@ let build_lts partition = | cp::t -> begin let p = List.hd cp in - let labs = + let labs = List.fold_left - (fun acc x -> + (fun acc x -> match List.filter (fun (src,_,_) -> src = p) x with | [] -> acc | t::_ -> t::acc ) [] lstates - in let transs' = + in let transs' = List.fold_left (fun acc (_,lbl,dst) -> let cdl = List.filter diff --git a/src/Obdd.ml b/src/Obdd.ml index f3e4ff5..79486f6 100644 --- a/src/Obdd.ml +++ b/src/Obdd.ml @@ -108,3 +108,4 @@ let inter = memo_rec2 ( ) let inter o1 o2 = inter (o1, o2) + From 10283b7574606e4164fb7feb04caba0f7394c912 Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Sun, 24 Nov 2013 19:58:31 +0100 Subject: [PATCH 37/42] Correction bug dans modality --- src/Control.ml | 1 + src/Local.ml | 31 +++++++++++++++++-------------- src/Parser.mly | 1 + src/examples/testtrace.ccs | 9 +++++++++ 4 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 src/examples/testtrace.ccs diff --git a/src/Control.ml b/src/Control.ml index 0f890aa..37b7332 100644 --- a/src/Control.ml +++ b/src/Control.ml @@ -365,3 +365,4 @@ let handle_check_local formula process = end let handle_check_global formula process = Global.check formula process + diff --git a/src/Local.ml b/src/Local.ml index cc1223c..79a1430 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -72,23 +72,23 @@ let rec check def_map prop_map trace formula nproc = let rec check_internal trace = function | FTrue -> (true, trace) | FNot formula -> - let okay1, trace1 = check_internal (( formula, nproc)::trace) formula in + let okay1, trace1 = check_internal ((formula, nproc)::trace) formula in (not okay1, trace1) | FFalse -> (false, trace) | FAnd (f1, f2) -> - let okay1, trace1 = check_internal (( f1, nproc)::trace) f1 in + let okay1, trace1 = check_internal ((f1, nproc)::trace) f1 in if not okay1 then okay1, trace1 else - let okay2, trace2 = check_internal (( f2, nproc)::trace1) f2 in + let okay2, trace2 = check_internal ((f2, nproc)::trace1) f2 in (okay1 && okay2, trace2) | FOr (f1, f2) -> - let okay1, trace1 = check_internal (( f1, nproc)::trace) f1 in + let okay1, trace1 = check_internal ((f1, nproc)::trace) f1 in if okay1 then okay1, trace else - let okay2, trace2 = check_internal (( f2, nproc)::trace1) f2 in + let okay2, trace2 = check_internal ((f2, nproc)::trace1) f2 in (okay1 || okay2, trace2) | FImplies (f1, f2) -> - let okay1, trace1 = check_internal (( f1, nproc)::trace) f1 in + let okay1, trace1 = check_internal ((f1, nproc)::trace) f1 in if not okay1 then not okay1, trace1 else - let okay2, trace2 = check_internal (( f2, nproc)::trace1) f2 in + let okay2, trace2 = check_internal ((f2, nproc)::trace1) f2 in (not okay1 || okay2, trace2) | FModal (modality, formula) -> check_modality def_map prop_map trace modality formula nproc @@ -106,30 +106,33 @@ let rec check def_map prop_map trace formula nproc = | FMu (x, env, mu_formula) -> let formula' = FNot (FNu (x, env, FNot (beta_reduce mu_formula x (FNot (FVar x))))) - in check_internal (( formula', nproc)::trace) formula' + in check_internal ((formula', nproc)::trace) formula' | FNu (_, env, _) when List.mem nproc env -> (true, trace) | FNu (x, env, formula) -> let reduced_formula = beta_reduce formula x @@ FNu(x, nproc::env, formula) in - check_internal (( reduced_formula, nproc)::trace) reduced_formula + check_internal ((reduced_formula, nproc)::trace) reduced_formula in check_internal trace formula and check_modality def_map prop_map trace modality formula process = let ts = transitions_of def_map process in - let operator = match modality with - | _, Necessity, _ -> (&&) - | _, Possibly, _ -> (||) + let operator, acc_init = match modality with + | _, Necessity, _ -> (&&), true + | _, Possibly, _ -> (||), false in let folding element (acc_okay, acc_trace) = let okay1, trace1 = - check def_map prop_map ((formula, process)::acc_trace) formula element + check def_map prop_map ((formula, element)::acc_trace) formula element in + Printf.printf "here : %s\n" @@ string_of_bool okay1; + Printf.printf "here : %s\n" @@ string_of_bool acc_okay; + Printf.printf "here : %s\n\n" @@ string_of_bool (operator okay1 acc_okay); (operator okay1 acc_okay, trace1) in - PSet.fold folding (next_process_set def_map modality ts) (false, trace) + PSet.fold folding (next_process_set def_map modality ts) (acc_init, trace) and check_prop_call def_map prop_map prop_name trace formula params process = diff --git a/src/Parser.mly b/src/Parser.mly index b38e38d..09b920e 100644 --- a/src/Parser.mly +++ b/src/Parser.mly @@ -369,6 +369,7 @@ formula: | TRUE { FTrue } | FALSE { FFalse } + | LPAREN formula RPAREN { $2 } | formula AND formula { FAnd ($1,$3) } | formula OR formula { FOr ($1,$3) } | formula IMPLIES formula { FImplies ($1,$3) } diff --git a/src/examples/testtrace.ccs b/src/examples/testtrace.ccs new file mode 100644 index 0000000..8123c60 --- /dev/null +++ b/src/examples/testtrace.ccs @@ -0,0 +1,9 @@ +def P = a!,Q; +def Q = b!,P; +prop B = Nu(X).X; +checklocal B |- P; + +prop B1 = Nu(X).X; +checklocal B1 |- P; + +checklocal B or B1 |- P; From 7c90e281f7791d9f19effc78afcfc9333b9a4a6e Mon Sep 17 00:00:00 2001 From: remyzorg Date: Sun, 24 Nov 2013 20:29:18 +0100 Subject: [PATCH 38/42] debut rapport --- rapport/rapport.tex | 77 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/rapport/rapport.tex b/rapport/rapport.tex index 7bf2c86..f5c649c 100644 --- a/rapport/rapport.tex +++ b/rapport/rapport.tex @@ -39,12 +39,85 @@ \section{Introduction} -\section{$\mu$-Calcul par valeur} +Le logiciel Pave est un interprète pour un langage de modélisation de +système concurrent : CCS\footnote{Calculus of communicating +systems}. Il a été développé par les différentes générations +d'étudiants de l'UE SACC. La dernière pierre ajoutée à cet édifice est +la prise en main du $\mu$-Calcul dans l'interprète. Après l'ajout de +sucre syntaxique, l'objectif est d'implémenter un algorithme de +vérification d'un processus décrit en CCS à partir d'une formule +$\mu$-Calcul. + +On appelle cette vérification du \emph{model-checking}. On vérifie +que la description d'un programme est fidèle à sa spécification de +façon automatique. On présentera ici deux algorithme pour le +model-checking +à hauteur de notre compréhension : local et global. Seul +l'algorithme local a été écrit dans la version proposée par notre binôme. + +%% \section{$\mu$-Calcul par valeur} \section{Model checking local} +L'algorithme de model checking local a été inspiré par Glynn Winskel +dans \emph{Topics in concurrency}. Le principe est en fait assez +simple. On parcourt récursivement le processus et la formule en +vérifiant que chaque état du processus vérifie la formule. On traite +les cas triviaux en traduisant simplement l'opérateur $\mu$-calcul en +opérateur booléen inductivement. + +\begin{lstlisting}[language=caml] +let rec check def_map prop_map formula nproc = + let rec check_internal = function + | FTrue -> true + | FFalse -> false + | FNot formula -> not @@ check_internal formula + | FAnd (f1, f2) -> check_internal f1 && check_internal f2 + | FOr (f1, f2) -> check_internal f1 || check_internal f2 + | FImplies (f1, f2) -> check_internal f1 |> not || check_internal f2 + | ... +\end{lstlisting} + +On considère un processus $p$. Dans le cas des modalités, on récupère +toutes les dérivations possibles de $p$ dont la transition est +étiquetée par la bonne action (sans oublier \texttt{<>} +et \texttt{[]}) et on construit un ensemble de processus qui +correspond aux suivants de $p$. On appelle récursivement notre +fonction \texttt{check} sur les éléments de cet ensemble avec la +formule sans la modalité. + +La partie concernant les points fixes n'était pas triviale. L'idée est +de définir une fonction de \empg{beta-reduction} et de remplacer +récursivement le résultat courant dans l'appel suivant. + +\begin{lstlisting}[language=caml] +let beta_reduce in_formula expected_var replacement = + let rec beta_reduce in_formula = + match in_formula with + | FTrue | FFalse -> in_formula + | FAnd (f1, f2) -> FAnd(beta_reduce f1, beta_reduce f2) + | FOr (f1, f2) -> FOr(beta_reduce f1, beta_reduce f2) + | FImplies (f1, f2) -> FImplies(beta_reduce f1, beta_reduce f2) + | FModal (modality, formula) -> FModal(modality, beta_reduce formula) + | FInvModal (modality, formula) -> FInvModal(modality, beta_reduce formula) + | FProp _ -> in_formula + | FVar var when var = expected_var -> replacement + | FVar _ -> in_formula + | FMu (x, env, formula) -> FMu(x, env, beta_reduce formula) + | FNu (x, env, formula) -> FNu(x, env, beta_reduce formula) + | FNot formula -> FNot (beta_reduce formula) + in + beta_reduce in_formula +\end{lstlisting} + + \section{Model checking global} +Nous avons été arrêtés pour des questions de compréhension de +l'algorithme. + + + \section{Exemples} \end{document} @@ -52,3 +125,5 @@ \section{Exemples} # Local Variables: # compile-command: "rubber -d rapport.tex" # End: + +%% LocalWords: Glynn Winskel Topics concurrency From 9ff405d10a81433f373184d0813754b2bf12b4cd Mon Sep 17 00:00:00 2001 From: remyzorg Date: Sun, 24 Nov 2013 21:48:17 +0100 Subject: [PATCH 39/42] rapport suite --- rapport/rapport.tex | 24 +++++++++++++++++------- src/Local.ml | 21 +++++++++++---------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/rapport/rapport.tex b/rapport/rapport.tex index f5c649c..457485f 100644 --- a/rapport/rapport.tex +++ b/rapport/rapport.tex @@ -48,10 +48,10 @@ \section{Introduction} vérification d'un processus décrit en CCS à partir d'une formule $\mu$-Calcul. -On appelle cette vérification du \emph{model-checking}. On vérifie +On appelle cette vérification du \emph{Model Checking}. On vérifie que la description d'un programme est fidèle à sa spécification de façon automatique. On présentera ici deux algorithme pour le -model-checking +Model Checking à hauteur de notre compréhension : local et global. Seul l'algorithme local a été écrit dans la version proposée par notre binôme. @@ -63,7 +63,7 @@ \section{Model checking local} dans \emph{Topics in concurrency}. Le principe est en fait assez simple. On parcourt récursivement le processus et la formule en vérifiant que chaque état du processus vérifie la formule. On traite -les cas triviaux en traduisant simplement l'opérateur $\mu$-calcul en +les cas triviaux en traduisant simplement l'opérateur $\mu$-Calcul en opérateur booléen inductivement. \begin{lstlisting}[language=caml] @@ -87,7 +87,7 @@ \section{Model checking local} formule sans la modalité. La partie concernant les points fixes n'était pas triviale. L'idée est -de définir une fonction de \empg{beta-reduction} et de remplacer +de définir une fonction de \emph{beta-reduction} et de remplacer récursivement le résultat courant dans l'appel suivant. \begin{lstlisting}[language=caml] @@ -111,11 +111,21 @@ \section{Model checking local} \end{lstlisting} -\section{Model checking global} -Nous avons été arrêtés pour des questions de compréhension de -l'algorithme. +\section{Model Checking global} +L'algorithme global pour le Model Checking a, lui, été inspiré par +Sergey Berezin dans \emph{Model Checking algorithms for +$\mu$-Calculus} L'implémentation de cet algorithme dans le projet a +été arrêtée par manque de compréhension de l'article présenté et de +l'idée même de la méthode dite globale. Nous n'avons pas compris les +paramètres et ce qu'était sensé rendre l'algorithme. + +Notre première démarche a été de vouloir le résoudre avec la méthode +proposée des BDD, d'où la présence du module correspondant. La +traduction d'une formule vers un BDD demandait préalablement d'avoir +compris l'algorithme simple. Nous avons donc tenté cette approche sans +plus de succès. \section{Exemples} diff --git a/src/Local.ml b/src/Local.ml index d5dc169..4386e79 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -9,9 +9,9 @@ type error = exception Error of error -(* Trace, tuple of labels visited and +(* Trace, tuple of labels visited and process remaining to execute *) -type trace = (formula * Normalize.nprocess) list +type trace = (formula * Normalize.nprocess) list let print_error = function | Unbound_Proposition s -> Printf.printf "unbound proposition %s\n" s @@ -19,7 +19,8 @@ let print_error = function Printf.printf "unmatching length on proposition %s\n" s let transitions_of def_map nproc = Semop.derivatives def_map nproc -let weak_transitions_of def_map nproc = Semop.weak_derivatives true def_map nproc +let weak_transitions_of def_map nproc = + Semop.weak_derivatives true def_map nproc let check_label_prefixes lbl pref = @@ -78,25 +79,25 @@ let rec check def_map prop_map trace formula nproc = let okay1, trace1 = check_internal ((formula, nproc)::trace) formula in (not okay1, trace1) | FFalse -> (false, trace) - | FAnd (f1, f2) -> + | FAnd (f1, f2) -> let okay1, trace1 = check_internal ((f1, nproc)::trace) f1 in if not okay1 then okay1, trace1 else let okay2, trace2 = check_internal ((f2, nproc)::trace1) f2 in (okay1 && okay2, trace2) - | FOr (f1, f2) -> + | FOr (f1, f2) -> let okay1, trace1 = check_internal ((f1, nproc)::trace) f1 in if okay1 then okay1, trace else let okay2, trace2 = check_internal ((f2, nproc)::trace1) f2 in (okay1 || okay2, trace2) - | FImplies (f1, f2) -> + | FImplies (f1, f2) -> let okay1, trace1 = check_internal ((f1, nproc)::trace) f1 in if not okay1 then not okay1, trace1 else let okay2, trace2 = check_internal ((f2, nproc)::trace1) f2 in - (not okay1 || okay2, trace2) + (not okay1 || okay2, trace2) | FModal (modality, formula) -> check_modality def_map prop_map trace modality formula nproc | FInvModal (modality, formula) -> - let okay1, trace1 = + let okay1, trace1 = check_modality def_map prop_map trace modality formula nproc in (not okay1, trace1) @@ -127,7 +128,7 @@ and check_modality def_map prop_map trace modality formula process = | _, Possibly, _ -> (||), false in let folding element (acc_okay, acc_trace) = - let okay1, trace1 = + let okay1, trace1 = check def_map prop_map ((formula, element)::acc_trace) formula element in Printf.printf "here : %s\n" @@ string_of_bool okay1; @@ -154,5 +155,5 @@ and check_prop_call def_map prop_map prop_name trace formula params process = beta_reduce formula param_name param_content in let reduced_formula = List.fold_left reduce_param formula params_map in - check def_map prop_map ((reduced_formula, process)::trace) + check def_map prop_map ((reduced_formula, process)::trace) reduced_formula process From 03ee5ce88c3ec45485c897676c12b227a0c549eb Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Mon, 25 Nov 2013 00:14:01 +0100 Subject: [PATCH 40/42] Correction bug invmod --- src/Local.ml | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Local.ml b/src/Local.ml index d5dc169..5ee7a65 100644 --- a/src/Local.ml +++ b/src/Local.ml @@ -32,7 +32,7 @@ let check_label_prefixes lbl pref = -let rec next_process_set def_map modality transitions = +let rec next_process_set def_map modality inv transitions = let choose transition destination_set = let _, mod_to_check, destination = transition in match modality, mod_to_check with @@ -45,10 +45,14 @@ let rec next_process_set def_map modality transitions = (PrefixMap.fold (fun k d a -> PSet.union d a) (weak_transitions_of def_map destination) destination_set) - | (_, _, Rpref acts), label -> + | (_, _, Rpref acts), label when not inv -> if List.exists (check_label_prefixes label) acts then PSet.add destination destination_set else destination_set + | (_, _, Rpref acts), label when inv -> + if not @@ List.exists (check_label_prefixes label) acts then + PSet.add destination destination_set + else destination_set | _ -> destination_set in TSet.fold choose transitions PSet.empty @@ -94,14 +98,9 @@ let rec check def_map prop_map trace formula nproc = let okay2, trace2 = check_internal ((f2, nproc)::trace1) f2 in (not okay1 || okay2, trace2) | FModal (modality, formula) -> - check_modality def_map prop_map trace modality formula nproc + check_modality def_map prop_map trace modality false formula nproc | FInvModal (modality, formula) -> - let okay1, trace1 = - check_modality def_map prop_map trace modality formula nproc - in - (not okay1, trace1) - (* TODO : à vérifier la correctness *) - (* transitions : not ou not [a] *) + check_modality def_map prop_map trace modality true formula nproc | FProp (prop_name, params) -> check_prop_call def_map prop_map prop_name trace formula params nproc | FVar var -> @@ -120,7 +119,7 @@ let rec check def_map prop_map trace formula nproc = check_internal trace formula -and check_modality def_map prop_map trace modality formula process = +and check_modality def_map prop_map trace modality inv formula process = let ts = transitions_of def_map process in let operator, acc_init = match modality with | _, Necessity, _ -> (&&), true @@ -130,12 +129,9 @@ and check_modality def_map prop_map trace modality formula process = let okay1, trace1 = check def_map prop_map ((formula, element)::acc_trace) formula element in - Printf.printf "here : %s\n" @@ string_of_bool okay1; - Printf.printf "here : %s\n" @@ string_of_bool acc_okay; - Printf.printf "here : %s\n\n" @@ string_of_bool (operator okay1 acc_okay); (operator okay1 acc_okay, trace1) in - PSet.fold folding (next_process_set def_map modality ts) (acc_init, trace) + PSet.fold folding (next_process_set def_map modality inv ts) (acc_init, trace) and check_prop_call def_map prop_map prop_name trace formula params process = From 17100bfece7c7cf9ce9e07632709f5389625a67e Mon Sep 17 00:00:00 2001 From: Roven Gabriel Date: Mon, 25 Nov 2013 01:32:26 +0100 Subject: [PATCH 41/42] ajout dans rapport --- rapport/rapport.tex | 148 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/rapport/rapport.tex b/rapport/rapport.tex index 457485f..5552ecb 100644 --- a/rapport/rapport.tex +++ b/rapport/rapport.tex @@ -57,6 +57,26 @@ \section{Introduction} %% \section{$\mu$-Calcul par valeur} +\section{Modifications préliminaires} + +Avant de pouvoir implémenter la vérification de formules, nous avons effectués +quelques modifications dans l'optique d'améliorer l'utilisation de \texttt{pave}. +Nous avons amélioré l'affichage d'erreur qui non seulement n'arrête pas le +programme mais aussi est plus clair. + +\begin{verbatim} +> prop A = ; + ^ +Parser error at line 1 char 14: ~;~ +> +\end{verbatim} + +Nous avons modifié le parseur afin de pouvoir définir et vérifier des formules du +$\mu$-calcul et défini l'AST représentant les formules. Nous avons deux +particularités notables : les modalités sont représentées sous forme de triplet +\emph{(force, type, restriction)} et les opérations \texttt{Mu} et \texttt{Nu} +embarquent directement l'environement qui sera utilisé dans le \texttt{check local}. + \section{Model checking local} L'algorithme de model checking local a été inspiré par Glynn Winskel @@ -87,7 +107,7 @@ \section{Model checking local} formule sans la modalité. La partie concernant les points fixes n'était pas triviale. L'idée est -de définir une fonction de \emph{beta-reduction} et de remplacer +de définir une fonction de \emph{subtitution} et de remplacer récursivement le résultat courant dans l'appel suivant. \begin{lstlisting}[language=caml] @@ -110,7 +130,38 @@ \section{Model checking local} beta_reduce in_formula \end{lstlisting} +\subsection{Trace} + +Afin de mieux comprendre les raison d'une propriété fausse, nous avons ajouté la fonctionnalitée de trace. Ainsi, à chaque étape dans l'algorithme, la formule courante et processus courant sont stockés et restitué à la fin de l'opération. Ainsi nous obtenons le chemin qui arrive à la propriété fausse. + +\begin{verbatim} +checklocal ~(true) |- a!, a?; +Trace : + a!,a?,0 -| ~True + a?,0 -| True +FALSE PROPERTY +\end{verbatim} + +Dans cet exemple nous pouvons voir que c'est à l'étape verb|a?,0| associé à la formule verb|True| que la propriété devient fausse. +\begin{verbatim} +def P = a!,P + b!,P; +def D = P || c!; + +checklocal true |- D; +Trace : + D() -| True + (c!,0||P()) -| True + (c!,0||P()) -| True + (c!,0||P()) -| True + (c!,0||P()) -| True + P() -| True + P() -| True + P() -| True +FALSE PROPERTY +\end{verbatim} + +Unn autre exemple de trace qui nous permet de voir que \verb|c!| ne peut apparaitre qu'une seule fois, la consomation de cette action est clairement visible sur les processus utilisés à gauche. \section{Model Checking global} @@ -130,6 +181,101 @@ \section{Model Checking global} \section{Exemples} +\subsection{Définition de propriétés usuelles} +\begin{verbatim} +prop Exercice5 = Mu(X).true or (<.>true and [.]X): +prop Possibly(A) = Mu(X).A or <.>X; +prop Deadlock = [.]false; +prop Always(A) = Nu(X).A and [.]X; +prop Continue = <.>true; +prop Eventualy(A) = Mu(X).A or ([.]X and <.> true); +\end{verbatim} + +\subsection{Vérifications} +\begin{verbatim} +def D2 = a!,(b? + D2); +\end{verbatim} + +\begin{verbatim} +> checklocal Always(Continue) |- D2; +Trace : + D2() -| Always(Continue) + D2() -| Nu(X){()}.(Continue and [.]X) + D2() -| (Continue and [.]Nu(X){(D2())}.(Continue and [.]X)) + D2() -| Continue + D2() -| <.>True + (b?,0+D2()) -| True + D2() -| [.]Nu(X){(D2())}.(Continue and [.]X) + (b?,0+D2()) -| Nu(X){(D2())}.(Continue and [.]X) + (b?,0+D2()) -| (Continue and [.]Nu(X){((b?,0+D2()),D2())}.(Continue and [.]X)) + (b?,0+D2()) -| Continue + (b?,0+D2()) -| <.>True + 0 -| True + (b?,0+D2()) -| True + (b?,0+D2()) -| [.]Nu(X){((b?,0+D2()),D2())}.(Continue and [.]X) + 0 -| Nu(X){((b?,0+D2()),D2())}.(Continue and [.]X) + 0 -| (Continue and [.]Nu(X){(0,(b?,0+D2()),D2())}.(Continue and [.]X)) + 0 -| Continue + 0 -| <.>True + (b?,0+D2()) -| Nu(X){((b?,0+D2()),D2())}.(Continue and [.]X) +FALSE PROPERTY +\end{verbatim} + +Le processus ne s'execute pas à l'infini tout le temps. + +\begin{verbatim} +> checklocal Possibly(Continue) |- D2; +TRUE PROPERTY + +> checklocal Possibly(Deadlock) |- D2; +TRUE PROPERTY +\end{verbatim} + +Il est cependant possible qu'il s'execute à l'infini ou qu'il s'arrête. + +\begin{verbatim} +> checklocal Always(Deadlock) |- D2; +Trace : + D2() -| Always(Deadlock) + D2() -| Nu(X){()}.(Deadlock and [.]X) + D2() -| (Deadlock and [.]Nu(X){(D2())}.(Deadlock and [.]X)) + D2() -| Deadlock + D2() -| [.]False + (b?,0+D2()) -| False +FALSE PROPERTY + +\end{verbatim} + +Il ne s'arrête donc pas tout le temps. + +\begin{verbatim} +def E = d!,c!,e!; +def F = (c!,b!,a!,b! || c!,a!,b!); +def G = (c!,b!,a!,b! || c!,a!,b! || E); + +prop SuiteAAC = Possibly(true); + +> checklocal SuiteAAC |- F; +Trace : + F() -| SuiteAAC + F() -| Possibly(True) + F() -| Mu(X){()}.(True or <.>X) + F() -| not Nu(X){()}.not (True or <.>not X) + +[...] + 0 -| <.>not Nu(X){(0,b!,0,(b!,0||b!,0),(a!,b!,0||b!,0), + (b!,0||c!,a!,b!,0),(a!,b!,0||c!,a!,b!,0),(b!,a!,b!,0||c!,a!,b!,0),F()a}. + not (True or <.>not X) +FALSE PROPERTY + +> checklocal SuiteAAC |- G; +TRUE PROPERTY +\end{verbatim} + +Dans cet exemple nous pouvons observer que la séquence \texttt{a!,a!,c!} n'est +possible dans le processus F que si un troisième processus parallel E contenant +\texttt{c!} est défini. + \end{document} # Local Variables: From 134d702e2e955b78c12d2f5f57e664a5e14edf25 Mon Sep 17 00:00:00 2001 From: remyzorg Date: Mon, 25 Nov 2013 01:54:28 +0100 Subject: [PATCH 42/42] correction raide --- rapport/rapport.tex | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/rapport/rapport.tex b/rapport/rapport.tex index 5552ecb..05bc1dd 100644 --- a/rapport/rapport.tex +++ b/rapport/rapport.tex @@ -132,7 +132,11 @@ \section{Model checking local} \subsection{Trace} -Afin de mieux comprendre les raison d'une propriété fausse, nous avons ajouté la fonctionnalitée de trace. Ainsi, à chaque étape dans l'algorithme, la formule courante et processus courant sont stockés et restitué à la fin de l'opération. Ainsi nous obtenons le chemin qui arrive à la propriété fausse. +Afin de mieux comprendre les raison d'une propriété fausse, nous avons +ajouté la fonctionnalitée de trace. Ainsi, à chaque étape dans +l'algorithme, la formule courante et processus courant sont stockés et +restitués à la fin de l'opération. Ainsi nous obtenons le chemin qui +arrive à la propriété fausse. \begin{verbatim} checklocal ~(true) |- a!, a?; @@ -142,7 +146,9 @@ \subsection{Trace} FALSE PROPERTY \end{verbatim} -Dans cet exemple nous pouvons voir que c'est à l'étape verb|a?,0| associé à la formule verb|True| que la propriété devient fausse. +Dans cet exemple nous pouvons voir que c'est à +l'étape \verb|a?,0| associé à la +formule \verb|True| que la propriété devient fausse. \begin{verbatim} def P = a!,P + b!,P; @@ -161,7 +167,9 @@ \subsection{Trace} FALSE PROPERTY \end{verbatim} -Unn autre exemple de trace qui nous permet de voir que \verb|c!| ne peut apparaitre qu'une seule fois, la consomation de cette action est clairement visible sur les processus utilisés à gauche. +Un autre exemple de trace qui nous permet de voir que \verb|c!| ne +peut apparaitre qu'une seule fois, la consomation de cette action est +clairement visible sur les processus utilisés à gauche. \section{Model Checking global} @@ -272,8 +280,9 @@ \subsection{Vérifications} TRUE PROPERTY \end{verbatim} -Dans cet exemple nous pouvons observer que la séquence \texttt{a!,a!,c!} n'est -possible dans le processus F que si un troisième processus parallel E contenant +Dans cet exemple nous pouvons observer que la +séquence \texttt{a!,a!,c!} n'est possible dans le processus F que si +un troisième processus parallel E contenant \texttt{c!} est défini. \end{document}