1 (********************************************************************)
3 (* The Why3 Verification Platform / The Why3 Development Team *)
4 (* Copyright 2010-2023 -- Inria - CNRS - Paris-Saclay University *)
6 (* This software is distributed under the terms of the GNU Lesser *)
7 (* General Public License version 2.1, with the special exception *)
8 (* on linking described in file LICENSE. *)
10 (********************************************************************)
17 let () = Exn_printer.register (fun fmt exn -> match exn with
18 | Error -> Format.pp_print_string fmt "syntax error"
21 let floc s e = Loc.extract (s,e)
22 let mk_id id s e = { id_str = id; id_ats = []; id_loc = floc s e }
23 let mk_pat d s e = { pat_desc = d; pat_loc = floc s e }
24 let mk_term d s e = { term_desc = d; term_loc = floc s e }
25 let mk_expr loc d = { expr_desc = d; expr_loc = loc }
26 let mk_stmt loc d = Dstmt { stmt_desc = d; stmt_loc = loc }
27 let mk_var id = mk_expr id.id_loc (Eident id)
29 let variant_union v1 v2 = match v1, v2 with
32 | _, ({term_loc = loc},_)::_ -> Loc.errorm ~loc
33 "multiple `variant' clauses are not allowed"
35 let get_op s e = Qident (mk_id (Ident.op_get "") s e)
36 let upd_op s e = Qident (mk_id (Ident.op_update "") s e)
39 sp_pre = []; sp_post = []; sp_xpost = [];
40 sp_reads = []; sp_writes = []; sp_alias = [];
42 sp_checkrw = false; sp_diverge = false; sp_partial = false;
45 let spec_union s1 s2 = {
46 sp_pre = s1.sp_pre @ s2.sp_pre;
47 sp_post = s1.sp_post @ s2.sp_post;
48 sp_xpost = s1.sp_xpost @ s2.sp_xpost;
49 sp_reads = s1.sp_reads @ s2.sp_reads;
50 sp_writes = s1.sp_writes @ s2.sp_writes;
51 sp_alias = s1.sp_alias @ s2.sp_alias;
52 sp_variant = variant_union s1.sp_variant s2.sp_variant;
53 sp_checkrw = s1.sp_checkrw || s2.sp_checkrw;
54 sp_diverge = s1.sp_diverge || s2.sp_diverge;
55 sp_partial = s1.sp_partial || s2.sp_partial;
61 PTtyvar { id_str = "a" ^ string_of_int !r; id_loc = loc; id_ats = [] }
63 let logic_type loc = function
64 | None -> fresh_type_var loc
67 let logic_param loc (id, ty) = id, logic_type loc ty
71 %token <string> INTEGER
72 %token <string> STRING
73 %token <Py_ast.binop> CMP
74 %token <string> IDENT QIDENT TVAR
75 %token DEF IF ELSE ELIF RETURN WHILE FOR IN AND OR NOT NONE TRUE FALSE PASS
76 %token FROM IMPORT BREAK CONTINUE
78 %token LEFTPAR RIGHTPAR LEFTSQ RIGHTSQ COMMA EQUAL COLON BEGIN END NEWLINE
79 PLUSEQUAL MINUSEQUAL TIMESEQUAL DIVEQUAL MODEQUAL
81 %token PLUS MINUS TIMES DIV MOD
83 %token INVARIANT VARIANT ASSUME ASSERT CHECK REQUIRES ENSURES LABEL
84 %token FUNCTION PREDICATE AXIOM LEMMA CONSTANT CALL
85 %token ARROW LARROW LRARROW FORALL EXISTS DOT THEN LET OLD AT BY SO
91 %right ARROW LRARROW BY SO
99 %nonassoc unary_minus prec_prefix_op
103 (* Transformations entries *)
104 %start <Why3.Ptree.term> term_eof
105 %start <Why3.Ptree.term list> term_comma_list_eof
106 %start <Why3.Ptree.ident list> ident_comma_list_eof
108 %type <Py_ast.file> file
109 %type <Py_ast.decl> stmt
116 | NEWLINE? dl=nonempty_list(decl) NEWLINE? EOF
129 | FROM m=ident IMPORT l=separated_list(COMMA, ident) NEWLINE
133 | CONSTANT NEWLINE id = ident EQUAL e = expr NEWLINE
137 | LEMMA id=ident COLON t=term NEWLINE
138 { Dprop (Decl.Plemma, id, t) }
139 | AXIOM id=ident COLON t=term NEWLINE
140 { Dprop (Decl.Paxiom, id, t) }
143 | FUNCTION id=ident LEFTPAR l=separated_list(COMMA, param) RIGHTPAR
144 ty=option(function_type) var = option(fp_variant) def=option(logic_body)
146 { let loc = floc $startpos $endpos in
147 Dlogic (id, List.map (logic_param loc) l, Some (logic_type loc ty),
149 | PREDICATE id=ident LEFTPAR l=separated_list(COMMA, param) RIGHTPAR
150 var=option(fp_variant) def=option(logic_body) NEWLINE
151 { let loc = floc $startpos $endpos in
152 Dlogic (id, List.map (logic_param loc) l, None, var, def) }
155 | LEFTBR VARIANT v=term RIGHTBR { v }
162 | id=ident ty=option(param_type)
173 /* Note: "list" is a legal type annotation in Python; we make it a
174 * polymorphic type "list 'a" in WhyML */
179 { if id.id_str = "list"
180 then PTtyapp (Qident id, [fresh_type_var (floc $startpos $endpos)])
181 else PTtyapp (Qident id, []) }
182 | id=ident LEFTSQ tyl=separated_nonempty_list(COMMA, typ) RIGHTSQ
184 if id.id_str = "Tuple" then PTtuple tyl else PTtyapp (Qident id, tyl)
189 DEF f = ident LEFTPAR x = separated_list(COMMA, param) RIGHTPAR
190 ty=option(function_type) COLON NEWLINE BEGIN s=spec l=body END
192 if f.id_str = "range" then
193 let loc = floc $startpos $endpos in
194 Loc.errorm ~loc "micro Python does not allow shadowing 'range'"
195 else if fct && ty = None then
196 let loc = floc $startpos $endpos in
197 Loc.errorm ~loc "a logical function should not return a unit type"
198 else Ddef (f, x, ty, s, l ty s, fct)
203 | FUNCTION NEWLINE { true }
204 | (* epsilon *) { false }
208 | nonempty_list(stmt)
211 { fun ty s -> [mk_stmt (floc $startpos $endpos) (Spass (ty, s))] }
214 | (* epsilon *) { empty_spec }
215 | single_spec spec { spec_union $1 $2 }
218 | REQUIRES t=term NEWLINE
219 { { empty_spec with sp_pre = [t] } }
220 | ENSURES e=ensures NEWLINE
221 { { empty_spec with sp_post = [floc $startpos(e) $endpos(e), e] } }
223 { { empty_spec with sp_variant = $1 } }
227 { let id = mk_id "result" $startpos $endpos in
228 [mk_pat (Pvar id) $startpos $endpos, $1] }
233 { mk_expr (floc $startpos $endpos) d }
239 | LEFTPAR e = expr RIGHTPAR
245 { mk_expr (floc $startpos $endpos) d }
250 e = expr_nt { e.expr_desc }
251 | e1 = expr_nt COMMA el = separated_list(COMMA, expr_nt)
256 e = expr_nt_desc { e }
257 | e = expr_nt COMMA el = separated_list(COMMA, expr_nt) { Etuple (e::el) }
262 { mk_expr (floc $startpos $endpos) d }
276 | e1 = expr_nt LEFTSQ e2 = expr_nt RIGHTSQ
278 | e1 = expr_nt LEFTSQ e2=option(expr_nt) COLON e3=option(expr_nt) RIGHTSQ
280 let f = mk_id "slice" $startpos $endpos in
281 let none = mk_expr (floc $startpos $endpos) Enone in
282 let e2, e3 = match e2, e3 with
283 | None, None -> none, none
284 | Some e, None -> e, none
285 | None, Some e -> none, e
286 | Some e, Some e' -> e, e'
290 | MINUS e1 = expr_nt %prec unary_minus
294 | e1 = expr_nt o = binop e2 = expr_nt
295 { Ebinop (o, e1, e2) }
296 | e1 = expr_nt TIMES e2 = expr_nt
297 { match e1.expr_desc with
298 | Elist [e1] -> Emake (e1, e2)
299 | _ -> Ebinop (Bmul, e1, e2) }
300 | e=expr_dot DOT f=ident LEFTPAR el=separated_list(COMMA, expr_nt) RIGHTPAR
303 | "pop" | "append" | "reverse" | "clear" | "copy" | "sort" ->
305 | m -> let loc = floc $startpos $endpos in
306 Loc.errorm ~loc "The method '%s' is not implemented" m
308 | f = ident LEFTPAR e = separated_list(COMMA, expr_nt) RIGHTPAR
310 | LEFTSQ l = separated_list(COMMA, expr_nt) RIGHTSQ
312 | e1=expr_nt IF c=expr_nt ELSE e2=expr_nt
329 | X { mk_stmt (floc $startpos $endpos) $1 }
333 | s = simple_stmt NEWLINE
335 | NEWLINE BEGIN l = nonempty_list(stmt) END
340 | located(stmt_desc) { $1 }
341 | s = simple_stmt NEWLINE { s }
344 | IF c = expr_nt COLON s1 = suite s2=else_branch
346 | WHILE e = expr_nt COLON b=loop_body
347 { let i, v, l = b in Swhile (e, i, v, l) }
348 | FOR x = ident IN e = expr COLON b=loop_body
349 { let i, _, l = b in Sfor (x, e, i, l) }
355 | ELSE COLON s2=suite
357 | ELIF c=expr_nt COLON s1=suite s2=else_branch
358 { [mk_stmt (floc $startpos $endpos) (Sif (c, s1, s2))] }
362 | s = simple_stmt NEWLINE
364 | NEWLINE BEGIN a=loop_annotation l=nonempty_list(stmt) END
370 | invariant loop_annotation
371 { let (i, v) = $2 in ($1::i, v) }
372 | variant loop_annotation
373 { let (i, v) = $2 in (i, variant_union $1 v) }
376 | INVARIANT i=term NEWLINE { i }
379 | VARIANT l=comma_list1(term) NEWLINE { List.map (fun t -> t, None) l }
381 simple_stmt: located(simple_stmt_desc) { $1 };
386 | lhs = expr EQUAL rhs = expr { Sassign (lhs, rhs) }
387 | id=ident o=binop_equal e=expr_nt
388 { let loc = floc $startpos $endpos in
390 mk_expr loc (Ebinop (o, mk_expr loc (Eident id), e))) }
391 | e0 = expr_nt LEFTSQ e1 = expr_nt RIGHTSQ o=binop_equal e2 = expr
393 let loc = floc $startpos $endpos in
394 let mk_expr_floc = mk_expr loc in
395 let id = mk_id "'i" $startpos $endpos in
396 let expr_id = mk_expr_floc (Eident id) in
397 let a = mk_id "'a" $startpos $endpos in
398 let expr_a = mk_expr_floc (Eident a) in
400 mk_expr_floc (Ebinop (o, mk_expr_floc (Eget(expr_a, expr_id)), e2)) in
402 Dstmt ({ stmt_desc = Sassign (mk_var a, e0); stmt_loc = loc }) in
404 Dstmt ({ stmt_desc = Sassign (mk_var id, e1); stmt_loc = loc }) in
406 Dstmt ({ stmt_desc = Sset (expr_a, expr_id, operation);
410 | k=assertion_kind t = term
414 | CALL f = ident LEFTPAR e = separated_list(COMMA, term) RIGHTPAR
415 { Scall_lemma (f, e) }
426 | MINUSEQUAL { Bsub }
428 | TIMESEQUAL { Bmul }
433 | ASSERT { Expr.Assert }
434 | ASSUME { Expr.Assume }
435 | CHECK { Expr.Check }
438 | id = IDENT { mk_id id $startpos $endpos }
441 | id = QIDENT { mk_id id $startpos $endpos }
444 | id = TVAR { mk_id id $startpos $endpos }
449 mk_term(X): d = X { mk_term d $startpos $endpos }
451 term_tuple: t = mk_term(term_tuple_) { t }
454 | t = term ; COMMA; lt=separated_list(COMMA, term)
458 term: t = mk_term(term_) { t }
462 { match $1 with (* break the infix relation chain *)
463 | Tinfix (l,o,r) -> Tinnfix (l,o,r)
464 | Tbinop (l,o,r) -> Tbinnop (l,o,r)
468 | OLD LEFTPAR t=term RIGHTPAR
469 { Tat (t, mk_id Dexpr.old_label $startpos($1) $endpos($1)) }
470 | AT LEFTPAR t=term COMMA l=ident RIGHTPAR
472 | o = prefix_op ; t = term %prec prec_prefix_op
473 { Tidapp (Qident o, [t]) }
474 | l = term ; o = bin_op ; r = term
476 | l = term ; o = infix_op_1 ; r = term
478 | l = term ; o = infix_op_234 ; r = term
479 { Tidapp (Qident o, [l; r]) }
480 | IF term THEN term ELSE term
482 | LET id=ident EQUAL t1=term IN t2=term
483 { Tlet (id, t1, t2) }
484 | q=quant l=comma_list1(param) DOT t=term
485 { let var (id, ty) = id.id_loc, Some id, false, ty in
486 Tquant (q, List.map var l, [], t) }
487 | id=ident LEFTPAR l=separated_list(COMMA, term) RIGHTPAR
488 { Tidapp (Qident id, l) }
491 | FORALL { Dterm.DTforall }
492 | EXISTS { Dterm.DTexists }
494 term_arg: mk_term(term_arg_) { $1 }
497 | quote_ident { Tident (Qident $1) }
498 | ident { Tident (Qident $1) }
499 | INTEGER { Tconst (Constant.ConstInt Number.(int_literal ILitDec ~neg:false $1)) }
506 | LEFTPAR term_tuple RIGHTPAR { $2.term_desc }
507 | term_arg LEFTSQ term RIGHTSQ
508 { Tidapp (get_op $startpos($2) $endpos($2), [$1;$3]) }
509 | term_arg LEFTSQ term LARROW term RIGHTSQ
510 { Tidapp (upd_op $startpos($2) $endpos($2), [$1;$3;$5]) }
511 | e1 = term_arg LEFTSQ e2=option(term) COLON e3=option(term) RIGHTSQ
513 let slice = mk_id "slice" $startpos $endpos in
514 let len = mk_id "len" $startpos $endpos in
515 let z = Tconst (Constant.int_const_of_int 0) in
516 let l = Tidapp(Qident len, [e1]) in
517 let z = mk_term z $startpos $endpos in
518 let l = mk_term l $startpos $endpos in
519 let e2, e3 = match e2, e3 with
521 | Some e, None -> e, l
522 | None, Some e -> z, e
523 | Some e, Some e' -> e, e'
525 Tidapp(Qident slice,[e1;e2;e3])
529 | ARROW { Dterm.DTimplies }
530 | LRARROW { Dterm.DTiff }
532 | AND { Dterm.DTand }
537 | c=CMP { let op = match c with
544 | Badd|Bsub|Bmul|Bdiv|Bmod|Band|Bor -> assert false in
545 mk_id (Ident.op_infix op) $startpos $endpos }
548 | MINUS { mk_id (Ident.op_prefix "-") $startpos $endpos }
550 %inline infix_op_234:
551 | DIV { mk_id (Ident.op_infix "//") $startpos $endpos }
552 | MOD { mk_id (Ident.op_infix "%") $startpos $endpos }
553 | PLUS { mk_id (Ident.op_infix "+") $startpos $endpos }
554 | MINUS { mk_id (Ident.op_infix "-") $startpos $endpos }
555 | TIMES { mk_id (Ident.op_infix "*") $startpos $endpos }
558 | separated_nonempty_list(COMMA, X) { $1 }
560 (* Parsing of a list of qualified identifiers for the ITP *)
562 (* parsing of a single term *)
565 | term NEWLINE EOF { $1 }
567 ident_comma_list_eof:
568 | comma_list1(ident) NEWLINE EOF { $1 }
571 | comma_list1(term) NEWLINE EOF { $1 }
572 (* we use single_term to avoid conflict with tuples, that
573 do not need parentheses *)