2017-01-29 15:52:12 +01:00
|
|
|
(********************************************************************)
|
|
|
|
|
(* *)
|
|
|
|
|
(* The Why3 Verification Platform / The Why3 Development Team *)
|
2022-04-26 16:33:42 +02:00
|
|
|
(* Copyright 2010-2022 -- Inria - CNRS - Paris-Saclay University *)
|
2017-01-29 15:52:12 +01:00
|
|
|
(* *)
|
|
|
|
|
(* This software is distributed under the terms of the GNU Lesser *)
|
|
|
|
|
(* General Public License version 2.1, with the special exception *)
|
|
|
|
|
(* on linking described in file LICENSE. *)
|
|
|
|
|
(* *)
|
|
|
|
|
(********************************************************************)
|
2017-01-29 13:02:04 +01:00
|
|
|
|
|
|
|
|
open Why3
|
2017-04-14 17:45:25 +02:00
|
|
|
open Pmodule
|
2017-01-31 20:52:16 +01:00
|
|
|
open Py_ast
|
2017-01-29 13:02:04 +01:00
|
|
|
open Ptree
|
2018-05-17 11:26:04 +02:00
|
|
|
open Wstdlib
|
2017-01-29 13:02:04 +01:00
|
|
|
|
2017-01-29 16:57:49 +01:00
|
|
|
let debug = Debug.register_flag "python"
|
2017-01-29 13:02:04 +01:00
|
|
|
~desc:"mini-python plugin debug flag"
|
2018-01-19 10:46:11 +01:00
|
|
|
|
|
|
|
|
(* NO! this will be executed at plugin load, thus
|
|
|
|
|
disabling the warning for ALL WHY3 USERS even if they don't
|
|
|
|
|
use the python front-end
|
2017-01-31 16:02:44 +01:00
|
|
|
let () = Debug.set_flag Dterm.debug_ignore_unused_var
|
2018-01-19 10:46:11 +01:00
|
|
|
*)
|
2017-01-29 13:02:04 +01:00
|
|
|
|
2017-02-01 13:48:49 +01:00
|
|
|
let mk_id ~loc name =
|
2018-06-01 21:03:01 +02:00
|
|
|
{ id_str = name; id_ats = []; id_loc = loc }
|
2017-01-29 15:52:12 +01:00
|
|
|
|
2020-06-24 22:19:17 +02:00
|
|
|
let id_infix ~loc s = mk_id ~loc (Ident.op_infix s)
|
|
|
|
|
let infix ~loc s = Qident (id_infix ~loc s)
|
2018-07-07 16:13:29 +02:00
|
|
|
let prefix ~loc s = Qident (mk_id ~loc (Ident.op_prefix s))
|
2018-07-17 21:39:24 +02:00
|
|
|
let get_op ~loc = Qident (mk_id ~loc (Ident.op_get ""))
|
|
|
|
|
let set_op ~loc = Qident (mk_id ~loc (Ident.op_set ""))
|
2017-01-30 19:12:37 +01:00
|
|
|
|
2017-02-01 13:48:49 +01:00
|
|
|
let mk_expr ~loc d =
|
2017-01-29 15:52:12 +01:00
|
|
|
{ expr_desc = d; expr_loc = loc }
|
2017-02-01 13:48:49 +01:00
|
|
|
let mk_pat ~loc d =
|
2017-01-31 20:52:16 +01:00
|
|
|
{ pat_desc = d; pat_loc = loc }
|
2017-01-29 15:52:12 +01:00
|
|
|
let mk_unit ~loc =
|
|
|
|
|
mk_expr ~loc (Etuple [])
|
2017-01-30 19:12:37 +01:00
|
|
|
let mk_var ~loc id =
|
|
|
|
|
mk_expr ~loc (Eident (Qident id))
|
|
|
|
|
let mk_ref ~loc e =
|
|
|
|
|
mk_expr ~loc (Eidapp (Qident (mk_id ~loc "ref"), [e]))
|
2017-01-30 22:53:30 +01:00
|
|
|
let array_set ~loc a i v =
|
2018-07-07 16:13:29 +02:00
|
|
|
mk_expr ~loc (Eidapp (set_op ~loc, [a; i; v]))
|
2017-11-23 19:17:25 +01:00
|
|
|
let constant ~loc i =
|
2019-09-19 20:14:54 +02:00
|
|
|
mk_expr ~loc (Econst (Constant.int_const_of_int i))
|
2017-11-23 19:17:25 +01:00
|
|
|
let constant_s ~loc s =
|
2019-09-19 20:14:54 +02:00
|
|
|
let int_lit = Number.(int_literal ILitDec ~neg:false s) in
|
|
|
|
|
mk_expr ~loc (Econst (Constant.ConstInt int_lit))
|
2017-01-30 19:12:37 +01:00
|
|
|
let len ~loc =
|
|
|
|
|
Qident (mk_id ~loc "len")
|
2021-06-23 17:33:19 +02:00
|
|
|
|
|
|
|
|
let break_id = "'Break"
|
|
|
|
|
let continue_id = "'Continue"
|
|
|
|
|
let return_id = "'Return"
|
|
|
|
|
|
2017-01-30 22:53:30 +01:00
|
|
|
let array_make ~loc n v =
|
2021-06-23 17:33:19 +02:00
|
|
|
mk_expr ~loc (Eidapp (Qdot (Qident (mk_id ~loc "Python"), mk_id ~loc "make"),
|
2017-01-30 22:53:30 +01:00
|
|
|
[n; v]))
|
2021-06-23 17:33:19 +02:00
|
|
|
let array_empty ~loc =
|
|
|
|
|
mk_expr ~loc (Eidapp (Qdot (Qident (mk_id ~loc "Python"), mk_id ~loc "empty"), [mk_unit ~loc]))
|
2019-07-06 14:54:56 +02:00
|
|
|
let set_ref id =
|
|
|
|
|
{ id with id_ats = ATstr Pmodule.ref_attr :: id.id_ats }
|
2017-01-29 15:52:12 +01:00
|
|
|
|
|
|
|
|
let empty_spec = {
|
|
|
|
|
sp_pre = [];
|
|
|
|
|
sp_post = [];
|
|
|
|
|
sp_xpost = [];
|
|
|
|
|
sp_reads = [];
|
|
|
|
|
sp_writes = [];
|
2017-12-13 18:47:53 +01:00
|
|
|
sp_alias = [];
|
2017-01-29 15:52:12 +01:00
|
|
|
sp_variant = [];
|
|
|
|
|
sp_checkrw = false;
|
|
|
|
|
sp_diverge = false;
|
2018-09-27 15:24:02 +02:00
|
|
|
sp_partial = false;
|
2017-01-29 15:52:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type env = {
|
2017-01-30 19:12:37 +01:00
|
|
|
vars: ident Mstr.t;
|
2017-01-29 15:52:12 +01:00
|
|
|
}
|
|
|
|
|
|
2017-01-30 19:12:37 +01:00
|
|
|
let empty_env =
|
2021-09-28 17:12:46 +02:00
|
|
|
{ vars = Mstr.empty }
|
2017-01-30 19:12:37 +01:00
|
|
|
|
2021-06-23 17:33:19 +02:00
|
|
|
let is_const (e: Py_ast.expr list) =
|
|
|
|
|
match List.nth e 2 with
|
|
|
|
|
| {Py_ast.expr_desc=Eint "1";_} -> 1
|
|
|
|
|
| {Py_ast.expr_desc=Eunop (Uneg, {Py_ast.expr_desc=Eint "1";_});_} -> -1
|
|
|
|
|
| _ -> 0
|
|
|
|
|
|
2017-01-30 19:12:37 +01:00
|
|
|
let add_var env id =
|
2021-09-28 17:12:46 +02:00
|
|
|
{ vars = Mstr.add id.id_str id env.vars }
|
2021-09-24 18:27:07 +02:00
|
|
|
let add_param env (id, _) =
|
|
|
|
|
add_var env id
|
2017-01-30 19:12:37 +01:00
|
|
|
|
2021-06-23 17:33:19 +02:00
|
|
|
let for_vars ~loc x =
|
|
|
|
|
let x = x.id_str in
|
|
|
|
|
mk_id ~loc (x ^ "'index"), mk_id ~loc (x ^ "'list")
|
2017-01-29 15:52:12 +01:00
|
|
|
|
2017-03-17 17:04:23 +01:00
|
|
|
let rec has_stmt p = function
|
|
|
|
|
| Dstmt s -> p s || begin match s.stmt_desc with
|
2021-06-23 17:33:19 +02:00
|
|
|
| Sbreak | Scontinue | Sreturn _ | Sassign _ | Slabel _
|
|
|
|
|
| Seval _ | Sset _ | Sblock _ | Sassert _ | Swhile _ -> false
|
2017-03-17 17:04:23 +01:00
|
|
|
| Sif (_, bl1, bl2) -> has_stmtl p bl1 || has_stmtl p bl2
|
|
|
|
|
| Sfor (_, _, _, bl) -> has_stmtl p bl end
|
|
|
|
|
| _ -> false
|
|
|
|
|
and has_stmtl p bl = List.exists (has_stmt p) bl
|
|
|
|
|
|
|
|
|
|
let rec expr_has_call id e = match e.Py_ast.expr_desc with
|
|
|
|
|
| Enone | Ebool _ | Eint _ | Estring _ | Py_ast.Eident _ -> false
|
|
|
|
|
| Emake (e1, e2) | Eget (e1, e2) | Ebinop (_, e1, e2) ->
|
|
|
|
|
expr_has_call id e1 || expr_has_call id e2
|
|
|
|
|
| Eunop (_, e1) -> expr_has_call id e1
|
2021-06-23 17:33:19 +02:00
|
|
|
| Edot (e, f, el) -> id.id_str = f.id_str || List.exists (expr_has_call id) (e::el)
|
2017-03-17 17:04:23 +01:00
|
|
|
| Ecall (f, el) -> id.id_str = f.id_str || List.exists (expr_has_call id) el
|
|
|
|
|
| Elist el -> List.exists (expr_has_call id) el
|
|
|
|
|
|
|
|
|
|
let rec stmt_has_call id s = match s.stmt_desc with
|
2021-06-23 17:33:19 +02:00
|
|
|
| Sbreak | Scontinue | Slabel _ | Sassert _ -> false
|
2017-03-17 17:04:23 +01:00
|
|
|
| Sreturn e | Sassign (_, e) | Seval e -> expr_has_call id e
|
2021-06-23 17:33:19 +02:00
|
|
|
| Sblock s -> block_has_call id s
|
2017-03-17 17:04:23 +01:00
|
|
|
| Sset (e1, e2, e3) ->
|
|
|
|
|
expr_has_call id e1 || expr_has_call id e2 || expr_has_call id e3
|
|
|
|
|
| Sif (e, s1, s2) -> expr_has_call id e || block_has_call id s1 || block_has_call id s2
|
2017-04-14 17:45:25 +02:00
|
|
|
| Sfor (_, e, _, s) | Swhile (e, _, _, s) -> expr_has_call id e || block_has_call id s
|
2017-03-17 17:04:23 +01:00
|
|
|
and block_has_call id = has_stmtl (stmt_has_call id)
|
2017-01-31 16:02:44 +01:00
|
|
|
|
2017-01-29 15:52:12 +01:00
|
|
|
let rec expr env {Py_ast.expr_loc = loc; Py_ast.expr_desc = d } = match d with
|
|
|
|
|
| Py_ast.Enone ->
|
|
|
|
|
mk_unit ~loc
|
|
|
|
|
| Py_ast.Ebool b ->
|
|
|
|
|
mk_expr ~loc (if b then Etrue else Efalse)
|
|
|
|
|
| Py_ast.Eint s ->
|
2017-11-23 19:17:25 +01:00
|
|
|
constant_s ~loc s
|
2017-01-29 15:52:12 +01:00
|
|
|
| Py_ast.Estring _s ->
|
2017-01-31 14:35:51 +01:00
|
|
|
mk_unit ~loc (*FIXME*)
|
2017-01-29 15:52:12 +01:00
|
|
|
| Py_ast.Eident id ->
|
2017-01-30 19:12:37 +01:00
|
|
|
if not (Mstr.mem id.id_str env.vars) then
|
2017-01-29 15:52:12 +01:00
|
|
|
Loc.errorm ~loc "unbound variable %s" id.id_str;
|
2019-07-06 14:54:56 +02:00
|
|
|
mk_expr ~loc (Eident (Qident id))
|
2017-01-29 15:52:12 +01:00
|
|
|
| Py_ast.Ebinop (op, e1, e2) ->
|
|
|
|
|
let e1 = expr env e1 in
|
|
|
|
|
let e2 = expr env e2 in
|
|
|
|
|
mk_expr ~loc (match op with
|
2017-04-14 17:45:25 +02:00
|
|
|
| Py_ast.Band -> Eand (e1, e2)
|
|
|
|
|
| Py_ast.Bor -> Eor (e1, e2)
|
2020-06-24 22:19:17 +02:00
|
|
|
| Py_ast.Badd -> Eidapp (infix ~loc "+", [e1; e2])
|
|
|
|
|
| Py_ast.Bsub -> Eidapp (infix ~loc "-", [e1; e2])
|
|
|
|
|
| Py_ast.Bmul -> Eidapp (infix ~loc "*", [e1; e2])
|
2017-04-25 17:02:29 +02:00
|
|
|
| Py_ast.Bdiv -> Eidapp (infix ~loc "//", [e1; e2])
|
2020-06-24 22:19:17 +02:00
|
|
|
| Py_ast.Bmod -> Eidapp (infix ~loc "%", [e1; e2])
|
|
|
|
|
| Py_ast.Beq -> Einnfix (e1, id_infix ~loc "=", e2)
|
|
|
|
|
| Py_ast.Bneq -> Einnfix (e1, id_infix ~loc "<>", e2)
|
|
|
|
|
| Py_ast.Blt -> Einnfix (e1, id_infix ~loc "<", e2)
|
|
|
|
|
| Py_ast.Ble -> Einnfix (e1, id_infix ~loc "<=", e2)
|
|
|
|
|
| Py_ast.Bgt -> Einnfix (e1, id_infix ~loc ">", e2)
|
|
|
|
|
| Py_ast.Bge -> Einnfix (e1, id_infix ~loc ">=", e2)
|
2017-01-29 15:52:12 +01:00
|
|
|
)
|
|
|
|
|
| Py_ast.Eunop (Py_ast.Uneg, e) ->
|
|
|
|
|
mk_expr ~loc (Eidapp (prefix ~loc "-", [expr env e]))
|
|
|
|
|
| Py_ast.Eunop (Py_ast.Unot, e) ->
|
2020-06-24 22:19:17 +02:00
|
|
|
mk_expr ~loc (Enot (expr env e))
|
2021-06-23 17:33:19 +02:00
|
|
|
|
|
|
|
|
| Py_ast.Edot (e, f, el) ->
|
|
|
|
|
let el = List.map (expr env) (e::el) in
|
|
|
|
|
let id = Qdot (Qident (mk_id ~loc "Python"), f) in
|
|
|
|
|
mk_expr ~loc (Eidapp (id, el))
|
|
|
|
|
|
|
|
|
|
| Py_ast.Ecall ({id_str="slice"} as id, [e1;e2;e3]) ->
|
|
|
|
|
|
|
|
|
|
let zero = expr env ({Py_ast.expr_loc = loc; Py_ast.expr_desc = Eint "0"}) in
|
|
|
|
|
let e1' = mk_id ~loc "e1'" in
|
|
|
|
|
let e1_var = mk_var ~loc e1' in
|
|
|
|
|
|
|
|
|
|
let len = mk_expr ~loc (Eidapp (Qident (mk_id ~loc "len"), [e1_var])) in
|
|
|
|
|
|
|
|
|
|
let e2, e3 = match e2, e3 with
|
|
|
|
|
| {Py_ast.expr_desc=Py_ast.Enone}, {Py_ast.expr_desc=Py_ast.Enone} -> zero, len
|
|
|
|
|
| _ , {Py_ast.expr_desc=Py_ast.Enone} -> expr env e2, len
|
|
|
|
|
| {Py_ast.expr_desc=Py_ast.Enone}, _ -> zero, expr env e3
|
|
|
|
|
| _ , _ -> expr env e2, expr env e3
|
|
|
|
|
in
|
|
|
|
|
|
|
|
|
|
let id = Qdot (Qident (mk_id ~loc "Python"), id) in
|
|
|
|
|
|
|
|
|
|
mk_expr ~loc (Elet (e1', false, Expr.RKnone, expr env e1,
|
|
|
|
|
mk_expr ~loc (Eidapp (id, [e1_var;e2;e3]))
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
| Py_ast.Ecall ({id_str="range"} as id, els) when List.length els < 4 ->
|
|
|
|
|
let zero = {Py_ast.expr_loc = loc; Py_ast.expr_desc = Eint "0"} in
|
|
|
|
|
let from_to_step, id = match els with
|
|
|
|
|
| [e] -> [zero; e], id
|
|
|
|
|
| [e1;e2] -> [e1; e2], id
|
|
|
|
|
| [e1;e2;e3] -> [e1; e2; e3], mk_id ~loc "range3"
|
|
|
|
|
| _ -> assert false
|
|
|
|
|
in
|
|
|
|
|
let el = List.map (expr env) from_to_step in
|
|
|
|
|
mk_expr ~loc (Eidapp (Qident id, el))
|
|
|
|
|
|
2017-01-31 18:53:35 +01:00
|
|
|
| Py_ast.Ecall ({id_str="print"}, el) ->
|
|
|
|
|
let eval res e =
|
2017-04-14 17:45:25 +02:00
|
|
|
mk_expr ~loc (Elet (mk_id ~loc "_", false, Expr.RKnone, expr env e, res)) in
|
2017-01-31 18:53:35 +01:00
|
|
|
List.fold_left eval (mk_unit ~loc) el
|
2017-01-30 07:57:19 +01:00
|
|
|
| Py_ast.Ecall (id, el) ->
|
2019-07-06 12:09:08 +02:00
|
|
|
let el = if el = [] then [mk_unit ~loc] else List.map (expr env) el in
|
|
|
|
|
mk_expr ~loc (Eidapp (Qident id, el))
|
2017-01-30 22:53:30 +01:00
|
|
|
| Py_ast.Emake (e1, e2) -> (* [e1]*e2 *)
|
|
|
|
|
array_make ~loc (expr env e2) (expr env e1)
|
|
|
|
|
| Py_ast.Elist [] ->
|
2021-06-23 17:33:19 +02:00
|
|
|
array_empty ~loc
|
2017-01-30 22:53:30 +01:00
|
|
|
| Py_ast.Elist el ->
|
|
|
|
|
let n = List.length el in
|
2017-11-07 10:27:15 +01:00
|
|
|
let n = constant ~loc n in
|
2017-01-30 22:53:30 +01:00
|
|
|
let id = mk_id ~loc "new array" in
|
2017-11-07 10:27:15 +01:00
|
|
|
mk_expr ~loc (Elet (id, false, Expr.RKnone, array_make ~loc n (constant ~loc 0),
|
2017-01-30 22:53:30 +01:00
|
|
|
let i = ref (-1) in
|
|
|
|
|
let init seq e =
|
2017-11-07 10:27:15 +01:00
|
|
|
incr i; let i = constant ~loc !i in
|
2017-01-30 22:53:30 +01:00
|
|
|
let assign = array_set ~loc (mk_var ~loc id) i (expr env e) in
|
|
|
|
|
mk_expr ~loc (Esequence (assign, seq)) in
|
|
|
|
|
List.fold_left init (mk_var ~loc id) el))
|
2017-01-30 07:57:19 +01:00
|
|
|
| Py_ast.Eget (e1, e2) ->
|
2018-07-07 16:13:29 +02:00
|
|
|
mk_expr ~loc (Eidapp (get_op ~loc, [expr env e1; expr env e2]))
|
2017-01-29 15:52:12 +01:00
|
|
|
|
2017-03-17 17:04:23 +01:00
|
|
|
let no_params ~loc = [loc, None, false, Some (PTtuple [])]
|
|
|
|
|
|
2021-06-23 17:33:19 +02:00
|
|
|
let mk_for_params exps loc env =
|
|
|
|
|
|
|
|
|
|
let mk_op1 op ub = mk_expr ~loc (Eidapp (infix ~loc op, [expr env ub; constant ~loc 1])) in
|
|
|
|
|
let mk_minus1 ub = mk_op1 "-" ub in
|
|
|
|
|
let mk_plus1 ub = mk_op1 "+" ub in
|
|
|
|
|
|
|
|
|
|
match exps with
|
|
|
|
|
| [e1] ->
|
|
|
|
|
let zero = {Py_ast.expr_loc = loc; Py_ast.expr_desc = Eint "0"} in
|
|
|
|
|
expr env zero, mk_minus1 e1, Expr.To
|
|
|
|
|
| [e1;e2] ->
|
|
|
|
|
expr env e1, mk_minus1 e2, Expr.To
|
|
|
|
|
| [e1;e2;_] ->
|
|
|
|
|
begin match is_const exps with
|
|
|
|
|
| 1 -> expr env e1, mk_minus1 e2, Expr.To
|
|
|
|
|
| -1 -> expr env e1, mk_plus1 e2, Expr.DownTo
|
|
|
|
|
| _ -> assert false
|
|
|
|
|
end
|
|
|
|
|
| _ -> assert false
|
|
|
|
|
|
2017-01-29 15:52:12 +01:00
|
|
|
let rec stmt env ({Py_ast.stmt_loc = loc; Py_ast.stmt_desc = d } as s) =
|
|
|
|
|
match d with
|
2021-06-23 17:33:19 +02:00
|
|
|
| Py_ast.Sblock s ->
|
|
|
|
|
block env ~loc s
|
2017-01-29 15:52:12 +01:00
|
|
|
| Py_ast.Seval e ->
|
2021-06-23 17:33:19 +02:00
|
|
|
let id = mk_id ~loc "_'" in
|
|
|
|
|
mk_expr ~loc (Elet (id, false, Expr.RKnone, expr env e, mk_unit ~loc))
|
2017-01-29 15:52:12 +01:00
|
|
|
| Py_ast.Sif (e, s1, s2) ->
|
2017-01-30 09:08:52 +01:00
|
|
|
mk_expr ~loc (Eif (expr env e, block env ~loc s1, block env ~loc s2))
|
2017-01-31 20:52:16 +01:00
|
|
|
| Py_ast.Sreturn e ->
|
2021-06-23 17:33:19 +02:00
|
|
|
mk_expr ~loc (Eraise (Qident (mk_id ~loc return_id), Some (expr env e)))
|
2017-01-29 15:52:12 +01:00
|
|
|
| Py_ast.Sassign (id, e) ->
|
|
|
|
|
let e = expr env e in
|
2017-01-30 19:12:37 +01:00
|
|
|
if Mstr.mem id.id_str env.vars then
|
2017-01-29 15:52:12 +01:00
|
|
|
let x = let loc = id.id_loc in mk_expr ~loc (Eident (Qident id)) in
|
2018-07-07 16:13:29 +02:00
|
|
|
mk_expr ~loc (Einfix (x, mk_id ~loc (Ident.op_infix ":="), e))
|
2017-01-29 15:52:12 +01:00
|
|
|
else
|
2017-03-17 17:04:23 +01:00
|
|
|
block env ~loc [Dstmt s]
|
2017-01-30 09:52:35 +01:00
|
|
|
| Py_ast.Sset (e1, e2, e3) ->
|
2017-01-30 22:53:30 +01:00
|
|
|
array_set ~loc (expr env e1) (expr env e2) (expr env e3)
|
2017-01-30 13:50:18 +01:00
|
|
|
| Py_ast.Sassert (k, t) ->
|
2019-07-06 14:54:56 +02:00
|
|
|
mk_expr ~loc (Eassert (k, t))
|
2017-04-14 17:45:25 +02:00
|
|
|
| Py_ast.Swhile (e, inv, var, s) ->
|
2021-06-23 17:33:19 +02:00
|
|
|
let id_b = mk_id ~loc break_id in
|
|
|
|
|
let id_c = mk_id ~loc continue_id in
|
|
|
|
|
let body = block env ~loc s in
|
|
|
|
|
let body = mk_expr ~loc (Eoptexn (id_c, Ity.MaskVisible, body)) in
|
|
|
|
|
let loop = mk_expr ~loc (Ewhile (expr env e, inv, var, body)) in
|
|
|
|
|
mk_expr ~loc (Eoptexn (id_b, Ity.MaskVisible, loop))
|
2017-01-31 16:02:44 +01:00
|
|
|
| Py_ast.Sbreak ->
|
2021-06-23 17:33:19 +02:00
|
|
|
mk_expr ~loc (Eraise (Qident (mk_id ~loc break_id), None))
|
|
|
|
|
| Py_ast.Scontinue ->
|
|
|
|
|
mk_expr ~loc (Eraise (Qident (mk_id ~loc continue_id), None))
|
2017-01-31 22:23:15 +01:00
|
|
|
| Py_ast.Slabel _ ->
|
|
|
|
|
mk_unit ~loc (* ignore lonely marks *)
|
2017-02-02 21:52:15 +01:00
|
|
|
(* make a special case for
|
2021-06-23 17:33:19 +02:00
|
|
|
for id in range(e1, [e2, e3])
|
|
|
|
|
*)
|
|
|
|
|
| Py_ast.Sfor (id, {Py_ast.expr_desc=Ecall ({id_str="range"}, exps)},
|
|
|
|
|
inv, body)
|
|
|
|
|
when (List.length exps = 3 && (let c = is_const exps in c = -1 || c = 1)) ->
|
|
|
|
|
|
|
|
|
|
let lb, ub, direction = mk_for_params exps loc env in
|
|
|
|
|
let body = block ~loc (add_var env id) body in
|
|
|
|
|
let body = mk_expr ~loc (Eoptexn (mk_id ~loc continue_id, Ity.MaskVisible, body)) in
|
|
|
|
|
let body = mk_expr ~loc (Elet (set_ref id, false, Expr.RKnone,
|
|
|
|
|
mk_ref ~loc (mk_var ~loc id), body)) in
|
|
|
|
|
let loop = mk_expr ~loc (Efor (id, lb, direction, ub, inv, body)) in
|
|
|
|
|
mk_expr ~loc (Eoptexn (mk_id ~loc break_id, Ity.MaskVisible, loop))
|
|
|
|
|
|
|
|
|
|
| Py_ast.Sfor (id, {Py_ast.expr_desc=Ecall ({id_str="range"}, exps)},
|
|
|
|
|
inv, body)
|
|
|
|
|
when (List.length exps < 3) ->
|
|
|
|
|
|
|
|
|
|
let lb, ub, direction = mk_for_params exps loc env in
|
|
|
|
|
let body = block ~loc (add_var env id) body in
|
|
|
|
|
let body = mk_expr ~loc (Eoptexn (mk_id ~loc continue_id, Ity.MaskVisible, body)) in
|
|
|
|
|
let body = mk_expr ~loc (Elet (set_ref id, false, Expr.RKnone,
|
|
|
|
|
mk_ref ~loc (mk_var ~loc id), body)) in
|
|
|
|
|
let loop = mk_expr ~loc (Efor (id, lb, direction, ub, inv, body)) in
|
|
|
|
|
mk_expr ~loc (Eoptexn (mk_id ~loc break_id, Ity.MaskVisible, loop))
|
2017-02-02 21:52:15 +01:00
|
|
|
(* otherwise, translate
|
|
|
|
|
for id in e:
|
|
|
|
|
#@ invariant inv
|
|
|
|
|
body
|
|
|
|
|
to
|
2017-01-30 19:12:37 +01:00
|
|
|
let l = e in
|
2021-06-23 17:33:19 +02:00
|
|
|
for i'index = 0 to len(l)-1 do
|
|
|
|
|
invariant { I }
|
|
|
|
|
let id = ref l[i'index] in
|
2017-02-02 21:52:15 +01:00
|
|
|
body
|
2017-01-30 19:12:37 +01:00
|
|
|
done
|
|
|
|
|
*)
|
2017-02-02 21:52:15 +01:00
|
|
|
| Py_ast.Sfor (id, e, inv, body) ->
|
2017-01-30 19:12:37 +01:00
|
|
|
let e = expr env e in
|
2021-06-23 17:33:19 +02:00
|
|
|
let i, l = for_vars ~loc id in
|
2017-11-07 10:27:15 +01:00
|
|
|
let lb = constant ~loc 0 in
|
2017-01-30 19:12:37 +01:00
|
|
|
let lenl = mk_expr ~loc (Eidapp (len ~loc, [mk_var ~loc l])) in
|
2017-11-07 10:27:15 +01:00
|
|
|
let ub = mk_expr ~loc (Eidapp (infix ~loc "-", [lenl; constant ~loc 1])) in
|
2021-06-23 17:33:19 +02:00
|
|
|
let li = mk_expr ~loc (Eidapp (get_op ~loc, [mk_var ~loc l; mk_var ~loc i])) in
|
|
|
|
|
let body = block ~loc (add_var env id) body in
|
|
|
|
|
let body = mk_expr ~loc (Eoptexn (mk_id ~loc continue_id, Ity.MaskVisible, body)) in
|
|
|
|
|
let body = mk_expr ~loc (Elet (set_ref id, false, Expr.RKnone,
|
|
|
|
|
mk_ref ~loc li, body)) in
|
|
|
|
|
let loop = mk_expr ~loc (Efor (i, lb, Expr.To, ub, inv, body)) in
|
|
|
|
|
let loop = mk_expr ~loc (Elet (l, false, Expr.RKnone, e, loop)) in
|
|
|
|
|
mk_expr ~loc (Eoptexn (mk_id ~loc break_id, Ity.MaskVisible, loop))
|
2017-01-29 15:52:12 +01:00
|
|
|
|
2017-02-01 13:48:49 +01:00
|
|
|
and block env ~loc = function
|
2017-01-29 15:52:12 +01:00
|
|
|
| [] ->
|
|
|
|
|
mk_unit ~loc
|
2017-03-17 17:04:23 +01:00
|
|
|
| Dstmt { stmt_loc = loc; stmt_desc = Slabel id } :: sl ->
|
2018-06-01 21:03:01 +02:00
|
|
|
mk_expr ~loc (Elabel (id, block env ~loc sl))
|
2017-03-17 17:04:23 +01:00
|
|
|
| Dstmt { Py_ast.stmt_loc = loc; stmt_desc = Py_ast.Sassign (id, e) } :: sl
|
2017-01-30 19:12:37 +01:00
|
|
|
when not (Mstr.mem id.id_str env.vars) ->
|
2017-01-29 15:52:12 +01:00
|
|
|
let e = expr env e in (* check e *before* adding id to environment *)
|
2017-01-30 19:12:37 +01:00
|
|
|
let env = add_var env id in
|
2019-07-06 14:54:56 +02:00
|
|
|
mk_expr ~loc (Elet (set_ref id, false, Expr.RKnone, mk_ref ~loc e, block env ~loc sl))
|
2017-03-17 17:04:23 +01:00
|
|
|
| Dstmt ({ Py_ast.stmt_loc = loc } as s) :: sl ->
|
2017-01-30 09:08:52 +01:00
|
|
|
let s = stmt env s in
|
2017-01-31 20:52:16 +01:00
|
|
|
if sl = [] then s else mk_expr ~loc (Esequence (s, block env ~loc sl))
|
2021-09-24 18:27:07 +02:00
|
|
|
| Ddef (id, idl, ty, sp, bl) :: sl ->
|
2017-04-24 15:43:45 +02:00
|
|
|
(* f(x1,...,xn): body ==>
|
|
|
|
|
let f x1 ... xn =
|
|
|
|
|
let x1 = ref x1 in ... let xn = ref xn in
|
|
|
|
|
try body with Return x -> x *)
|
2021-09-24 18:27:07 +02:00
|
|
|
let env' = List.fold_left add_param empty_env idl in
|
2017-04-24 15:43:45 +02:00
|
|
|
let body = block env' ~loc:id.id_loc bl in
|
2021-06-23 17:33:19 +02:00
|
|
|
let body =
|
2017-04-24 15:43:45 +02:00
|
|
|
let loc = id.id_loc in
|
2021-06-23 17:33:19 +02:00
|
|
|
let id = mk_id ~loc return_id in
|
|
|
|
|
{ body with expr_desc = Eoptexn (id, Ity.MaskVisible, body) } in
|
2021-09-24 18:27:07 +02:00
|
|
|
let local bl (id, _) =
|
2017-04-24 15:43:45 +02:00
|
|
|
let loc = id.id_loc in
|
|
|
|
|
let ref = mk_ref ~loc (mk_var ~loc id) in
|
2019-07-06 14:54:56 +02:00
|
|
|
mk_expr ~loc (Elet (set_ref id, false, Expr.RKnone, ref, bl)) in
|
2017-04-24 15:43:45 +02:00
|
|
|
let body = List.fold_left local body idl in
|
2021-09-24 18:27:07 +02:00
|
|
|
let param (id, ty) =
|
2021-09-28 14:36:47 +02:00
|
|
|
id.id_loc, Some id, false, ty in
|
2017-04-24 15:43:45 +02:00
|
|
|
let params = if idl = [] then no_params ~loc else List.map param idl in
|
2017-03-17 17:04:23 +01:00
|
|
|
let s = block env ~loc sl in
|
2019-09-18 17:24:19 +02:00
|
|
|
let p = mk_pat ~loc Pwild in
|
2017-04-24 15:43:45 +02:00
|
|
|
let e = if block_has_call id bl then
|
2021-09-24 18:27:07 +02:00
|
|
|
Erec ([id, false, Expr.RKnone, params, ty, p, Ity.MaskVisible, sp, body], s)
|
2017-04-24 15:43:45 +02:00
|
|
|
else
|
2021-09-24 18:27:07 +02:00
|
|
|
let e = Efun (params, ty, p, Ity.MaskVisible, sp, body) in
|
2017-04-24 15:43:45 +02:00
|
|
|
Elet (id, false, Expr.RKnone, mk_expr ~loc e, s) in
|
|
|
|
|
mk_expr ~loc e
|
2019-06-18 17:10:56 +02:00
|
|
|
| (Py_ast.Dimport _ | Py_ast.Dlogic _) :: sl ->
|
2017-03-17 17:04:23 +01:00
|
|
|
block env ~loc sl
|
2017-02-02 21:52:15 +01:00
|
|
|
|
2021-09-24 16:12:45 +02:00
|
|
|
let logic_param (id, ty) =
|
2021-09-28 14:36:47 +02:00
|
|
|
id.id_loc, Some id, false, ty
|
2017-03-17 17:04:23 +01:00
|
|
|
|
2017-04-24 15:43:45 +02:00
|
|
|
let logic = function
|
2021-09-30 10:43:02 +02:00
|
|
|
| Py_ast.Dlogic (id, idl, ty, def) ->
|
2017-03-17 17:04:23 +01:00
|
|
|
let d = { ld_loc = id.id_loc;
|
|
|
|
|
ld_ident = id;
|
|
|
|
|
ld_params = List.map logic_param idl;
|
2021-09-28 14:36:47 +02:00
|
|
|
ld_type = ty;
|
2021-09-30 10:43:02 +02:00
|
|
|
ld_def = def } in
|
2017-04-24 15:43:45 +02:00
|
|
|
Typing.add_decl id.id_loc (Dlogic [d])
|
2017-03-17 17:04:23 +01:00
|
|
|
| _ -> ()
|
|
|
|
|
|
2017-04-24 15:43:45 +02:00
|
|
|
let translate ~loc dl =
|
|
|
|
|
List.iter logic dl;
|
|
|
|
|
let bl = block empty_env ~loc dl in
|
2019-09-18 17:24:19 +02:00
|
|
|
let p = mk_pat ~loc Pwild in
|
|
|
|
|
let fd = Efun (no_params ~loc, None, p, Ity.MaskVisible, empty_spec, bl) in
|
2017-04-24 15:43:45 +02:00
|
|
|
let main = Dlet (mk_id ~loc "main", false, Expr.RKnone, mk_expr ~loc fd) in
|
|
|
|
|
Typing.add_decl loc main
|
2017-01-29 15:52:12 +01:00
|
|
|
|
2017-01-29 13:02:04 +01:00
|
|
|
let read_channel env path file c =
|
2019-04-12 09:34:59 +02:00
|
|
|
let f : Py_ast.file = Py_lexer.parse file c in
|
2017-01-29 13:02:04 +01:00
|
|
|
Debug.dprintf debug "%s parsed successfully.@." file;
|
|
|
|
|
let file = Filename.basename file in
|
|
|
|
|
let file = Filename.chop_extension file in
|
2017-04-05 14:22:24 +02:00
|
|
|
let name = Strings.capitalize file in
|
2017-01-29 13:02:04 +01:00
|
|
|
Debug.dprintf debug "building module %s.@." name;
|
2017-04-24 15:43:45 +02:00
|
|
|
Typing.open_file env path;
|
2022-06-22 18:17:43 +00:00
|
|
|
let loc = Loc.user_position file 0 0 0 0 in
|
2017-04-24 15:43:45 +02:00
|
|
|
Typing.open_module (mk_id ~loc name);
|
2017-01-29 15:52:12 +01:00
|
|
|
let use_import (f, m) =
|
2017-04-24 15:43:45 +02:00
|
|
|
let m = mk_id ~loc m in
|
2019-07-26 13:26:59 +02:00
|
|
|
let qid = Qdot (Qident (mk_id ~loc f), m) in
|
|
|
|
|
let decl = Ptree.Duseimport(loc,false,[(qid,None)]) in
|
|
|
|
|
Typing.add_decl loc decl in
|
2017-01-30 07:57:19 +01:00
|
|
|
List.iter use_import
|
2017-01-30 19:12:37 +01:00
|
|
|
["int", "Int"; "ref", "Refint"; "python", "Python"];
|
2017-04-24 15:43:45 +02:00
|
|
|
translate ~loc f;
|
|
|
|
|
Typing.close_module loc;
|
|
|
|
|
let mm = Typing.close_file () in
|
2017-01-29 13:02:04 +01:00
|
|
|
if path = [] && Debug.test_flag debug then begin
|
|
|
|
|
let add_m _ m modm = Ident.Mid.add m.mod_theory.Theory.th_name m modm in
|
2017-04-24 15:43:45 +02:00
|
|
|
let print_m _ m = Pmodule.print_module Format.err_formatter m in
|
|
|
|
|
Ident.Mid.iter print_m (Mstr.fold add_m mm Ident.Mid.empty)
|
2017-01-29 13:02:04 +01:00
|
|
|
end;
|
2017-04-24 15:43:45 +02:00
|
|
|
mm
|
2017-01-29 13:02:04 +01:00
|
|
|
|
|
|
|
|
let () =
|
|
|
|
|
Env.register_format mlw_language "python" ["py"] read_channel
|
|
|
|
|
~desc:"mini-Python format"
|
2019-10-04 15:51:14 +02:00
|
|
|
|
|
|
|
|
(* Python pretty-printer, to print tasks with a little bit
|
|
|
|
|
of Python syntax *)
|
2019-09-24 17:23:21 +02:00
|
|
|
|
2019-09-25 17:39:52 +02:00
|
|
|
open Term
|
2019-09-26 15:23:34 +02:00
|
|
|
open Format
|
|
|
|
|
open Pretty
|
|
|
|
|
|
|
|
|
|
(* python print_binop *)
|
|
|
|
|
let print_binop ~asym fmt = function
|
2021-09-10 14:11:14 +02:00
|
|
|
| Tand when asym -> pp_print_string fmt "&&"
|
|
|
|
|
| Tor when asym -> pp_print_string fmt "||"
|
|
|
|
|
| Tand -> pp_print_string fmt "and"
|
|
|
|
|
| Tor -> pp_print_string fmt "or"
|
|
|
|
|
| Timplies -> pp_print_string fmt "->"
|
|
|
|
|
| Tiff -> pp_print_string fmt "<->"
|
2019-09-25 17:39:52 +02:00
|
|
|
|
2019-09-23 15:58:34 +02:00
|
|
|
(* Register the transformations functions *)
|
|
|
|
|
let rec python_ext_printer print_any fmt a =
|
|
|
|
|
match a with
|
2019-09-26 15:23:34 +02:00
|
|
|
| Pp_term (t, pri) ->
|
2019-09-25 17:39:52 +02:00
|
|
|
begin match t.t_node with
|
|
|
|
|
| Tapp (ls, [t1; t2]) when ls_equal ls ps_equ ->
|
2019-09-25 17:23:49 +02:00
|
|
|
(* == *)
|
2019-09-26 15:23:34 +02:00
|
|
|
fprintf fmt (protect_on (pri > 0) "@[%a == %a@]")
|
|
|
|
|
(python_ext_printer print_any) (Pp_term (t1, 0))
|
|
|
|
|
(python_ext_printer print_any) (Pp_term (t2, 0))
|
2019-09-25 17:39:52 +02:00
|
|
|
| Tnot {t_node = Tapp (ls, [t1; t2]) } when ls_equal ls ps_equ ->
|
|
|
|
|
(* != *)
|
2019-09-26 15:23:34 +02:00
|
|
|
fprintf fmt (protect_on (pri > 0) "@[%a != %a@]")
|
|
|
|
|
(python_ext_printer print_any) (Pp_term (t1, 0))
|
|
|
|
|
(python_ext_printer print_any) (Pp_term (t2, 0))
|
|
|
|
|
| Tbinop (b, f1, f2) ->
|
|
|
|
|
(* and, or *)
|
|
|
|
|
let asym = Ident.Sattr.mem asym_split f1.t_attrs in
|
|
|
|
|
let p = prio_binop b in
|
|
|
|
|
fprintf fmt (protect_on (pri > p) "@[%a %a@ %a@]")
|
|
|
|
|
(python_ext_printer print_any) (Pp_term (f1, (p + 1)))
|
|
|
|
|
(print_binop ~asym) b
|
|
|
|
|
(python_ext_printer print_any) (Pp_term (f2, p))
|
2019-09-23 15:58:34 +02:00
|
|
|
| _ -> print_any fmt a
|
|
|
|
|
end
|
|
|
|
|
| _ -> print_any fmt a
|
|
|
|
|
|
ada_terms: Add task printer, transformation with args parser for Ada
This is inspired from the python printer/parser, the changes are:
- and, or, and then, or else are now supported instead of /\, \/, &&, ||
- different (LTGT) is now '/=' instead of '<>'
- allowing A'Last, A'First using "name" attributes
- allowing the notation "for all i in A .. B => stuff"
- allowing syntax for arrays as A(I) using new attributes "syntax" (also
the reason why we need to pass a name table to the parser)
- some constants are printed as "(cst:type)" (This is not real Ada syntax
and should probably be replaced by "type'(cst)"
This also adds a test at tests/ada_terms/print_test.adb
This adds new cases in any_pp: note that all these cases are needed as we
cannot use the legacyprinter for calls to, for instance, print_ls.
This would have the disadvantage that, when printed twice (reload), idents
gets reprinted which would change their disambiguation number (break at
every reload).
Also, pass the task to the printer in order to be able to detect which
lsymbols are used in record fields definitions.
Parser edition with respect to the Why3 parser:
- Removing qualification (not usable in transformation anyway)
- QUOTE disallowed in ident: still allowing it for type variable
- the ada parser uses a naming table during the parsing phase. The naming
table does not influence the parsing but is used to generate different
trees depending on the names recognized. It is useful to distinct a
function from an array in application as the syntax is the same.
Note that the naming_table is passed with a reference: we may want to
improve this in the future (no other known solution to the author of this
message).
New attributes appears:
- [@syntax:array:] followed by name of the getter function to be used:
this is used to know which function should be used as getter for this type
during the parsing.
- [@syntax:getter:] followed by its own name: this is used to know during
the printing if the function should be removed from the display to
simplify the notation. The name is checked in case attributes of a
specific ident are derived or copied to another one.
Add README with some changes in to be changed syntax
2019-11-25 11:57:11 +01:00
|
|
|
let () = Itp_server.add_registered_lang "python" (fun _ -> python_ext_printer)
|
2019-09-23 15:58:34 +02:00
|
|
|
|
2019-09-24 16:29:39 +02:00
|
|
|
let () = Args_wrapper.set_argument_parsing_functions "python"
|
2019-09-23 15:58:34 +02:00
|
|
|
~parse_term:(fun _ lb -> Py_lexer.parse_term lb)
|
|
|
|
|
~parse_term_list:(fun _ lb -> Py_lexer.parse_term_list lb)
|
|
|
|
|
~parse_list_ident:(fun lb -> Py_lexer.parse_list_ident lb)
|
|
|
|
|
(* TODO for qualids, add a similar funciton *)
|
|
|
|
|
~parse_qualid:(fun lb -> Lexer.parse_qualid lb)
|
|
|
|
|
~parse_list_qualid:(fun lb -> Lexer.parse_list_qualid lb)
|