Augment documentation of extraction to C
[why3.git] / src / extract / c.ml
blob6db73573a4c60456228e26f68cd1ab3cd2f98318
1 (********************************************************************)
2 (* *)
3 (* The Why3 Verification Platform / The Why3 Development Team *)
4 (* Copyright 2010-2023 -- Inria - CNRS - Paris-Saclay University *)
5 (* *)
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. *)
9 (* *)
10 (********************************************************************)
12 open Ident
14 exception Unsupported = Printer.Unsupported
16 let current_decl_name = ref ""
18 module C = struct
20 (* struct name, fields) *)
21 type struct_def = string * (string * ty) list
23 and ty =
24 | Tvoid
25 | Tsyntax of string * ty list
26 | Tptr of ty
27 | Tarray of ty * expr
28 | Tstruct of struct_def
29 | Tunion of ident * (ident * ty) list
30 | Tnamed of ident (* alias from typedef *)
31 | Tmutable of ty (* struct with a single mutable field *)
32 | Tnosyntax (* types that do not have a syntax, can't be printed *)
33 (* enum, const could be added *)
34 [@@warning "-37"]
36 (* int x=2, *y ... *)
37 and names = ty * (ident * expr) list
39 (* return type, parameter list. Variadic functions not handled. *)
40 and proto = ty * (ty * ident) list
42 and binop = Band | Bor | Beq | Bne | Bassign | Blt | Ble | Bgt | Bge
43 (* += and co. maybe to add *)
44 [@@warning "-37"]
46 and unop = Unot | Ustar | Uaddr | Upreincr | Upostincr | Upredecr | Upostdecr
47 [@@warning "-37"]
48 and expr =
49 | Enothing
50 | Eunop of unop * expr
51 | Ebinop of binop * expr * expr
52 | Equestion of expr * expr * expr (* c ? t : e *)
53 | Ecast of ty * expr
54 | Ecall of expr * expr list
55 | Econst of constant
56 | Evar of ident
57 | Elikely of expr (* __builtin_expect(e,1) *)
58 | Eunlikely of expr (* __builtin_expect(e,0) *)
59 | Esize_expr of expr
60 | Esize_type of ty
61 | Eindex of expr * expr (* Array access *)
62 | Edot of expr * string (* Field access with dot *)
63 | Earrow of expr * string (* Pointer access with arrow *)
64 | Esyntaxrename of string * expr list (* syntax val f "g" w/o params *)
65 | Esyntax of string * ty * (ty array) * (expr*ty) list * int list
66 (* template, type and type arguments of result, typed arguments, precedence level *)
68 and constant =
69 | Cint of string * Number.int_constant
70 | Cfloat of string
71 | Cchar of string
72 | Cstring of string
73 [@@warning "-37"]
75 type stmt =
76 | Snop
77 | Sexpr of expr
78 | Sblock of body
79 | Sseq of stmt * stmt
80 | Sif of expr * stmt * stmt
81 | Swhile of expr * stmt
82 | Sfor of expr * expr * expr * stmt
83 | Sbreak
84 | Sreturn of expr
86 and include_kind = Sys | Proj (* include <...> vs. include "..." *)
87 [@@warning "-37"]
88 and definition =
89 | Dfun of ident * proto * body
90 | Dinclude of ident * include_kind
91 | Dproto of ident * proto
92 | Ddecl of names
93 | Dextern of ty * ident
94 | Dstruct of struct_def
95 | Dstruct_decl of string
96 | Dtypedef of ty * ident
97 [@@warning "-37"]
99 and body = definition list * stmt
101 exception NotAValue
103 let rec is_nop = function
104 | Snop | Sexpr Enothing -> true
105 | Sblock ([], s) -> is_nop s
106 | Sseq (s1,s2) -> is_nop s1 && is_nop s2
107 | _ -> false
109 let is_one ic = BigInt.eq ic.Number.il_int BigInt.one
110 let is_zero ic = BigInt.eq ic.Number.il_int BigInt.zero
112 let is_true = function
113 | Sexpr(Econst(Cint (_,ic))) -> is_one ic
114 | _ -> false
116 let is_false = function
117 | Sexpr(Econst(Cint (_,ic))) -> is_zero ic
118 | _ -> false
120 (** [assignify v] transforms a statement that computes a value into
121 a statement that assigns that value to v *)
122 let rec assignify v = function
123 | Snop -> raise NotAValue
124 | Sexpr e -> Sexpr (Ebinop (Bassign, v, e))
125 | Sblock (ds, s) -> Sblock (ds, assignify v s)
126 | Sseq (s1, s2) when not (is_nop s2) -> Sseq (s1, assignify v s2)
127 | Sseq (s1,_) -> assignify v s1
128 | Sif (c,t,e) -> Sif (c, assignify v t, assignify v e)
129 | Swhile (c,s) -> Swhile (c, assignify v s) (* can this be a value ?*)
130 | Sfor (e0,e1,e2,s) -> Sfor (e0,e1,e2, assignify v s)
131 | Sbreak -> raise NotAValue
132 | Sreturn _ -> raise NotAValue
135 (** [get_last_expr] extracts the expression computed by the given statement.
136 This is needed when loop conditions are more complex than a
137 simple expression. *)
138 let rec get_last_expr = function
139 | Snop -> raise NotAValue
140 | Sexpr e -> Snop, e
141 | Sblock (ds,s) ->
142 let s',e = get_last_expr s in
143 Sblock(ds,s'), e
144 | Sseq (s1,s2) when not (is_nop s2) ->
145 let s', e = get_last_expr s2 in
146 Sseq(s1,s'), e
147 | Sseq (s1,_) -> get_last_expr s1
148 | Sif (c,Sexpr t,Sexpr e) -> Snop, Equestion(c,t,e)
149 | Sif _ -> raise (Unsupported "for/while (complex if)")
150 | Swhile (_c,_s) -> raise (Unsupported "for/while (while) {}")
151 | Sfor _ -> raise (Unsupported "for/while (for)")
152 | Sbreak -> raise NotAValue
153 | Sreturn _ -> raise NotAValue
155 (** [flatten_defs d s] appends all definitions in the statement [s] to d. *)
156 (* TODO check ident unicity ? *)
157 let rec flatten_defs d = function
158 | Sseq (s1,s2) ->
159 let d, s1' = flatten_defs d s1 in
160 let d, s2' = flatten_defs d s2 in
161 d, Sseq(s1',s2')
162 | Sblock (d',s) ->
163 let d',s' = flatten_defs d' s in
164 d@d', s'
165 | Sif (c,t,e) ->
166 let d, t' = flatten_defs d t in
167 let d, e' = flatten_defs d e in
168 d,Sif(c,t',e')
169 | Swhile (c,s) ->
170 let d, s' = flatten_defs d s in
171 d, Swhile (c, s')
172 | Sfor (e1,e2,e3,s) ->
173 let d, s' = flatten_defs d s in
174 d, Sfor(e1,e2,e3,s')
175 | Sbreak | Snop | Sexpr _ | Sreturn _ as s -> d, s
177 let rec group_defs_by_type l =
178 (* heuristic to reduce the number of lines of defs*)
179 let rec group_types t1 t2 =
180 match t1, t2 with
181 | Tvoid, Tvoid -> true
182 | Tsyntax (s1, l1), Tsyntax (s2, l2) ->
183 List.length l1 = List.length l2
184 && List.for_all2 group_types l1 l2
185 && s1 = s2
186 && (not (String.contains s1 '*'))
187 | Tptr t, Tptr t' -> group_types t t'
188 | Tarray _, _ -> false
189 | Tstruct (n1, _), Tstruct (n2, _) -> n1 = n2
190 | Tunion (id1, _), Tunion (id2, _) -> id_equal id1 id2
191 | Tnamed id1, Tnamed id2 -> id_equal id1 id2
192 | Tmutable t, Tmutable t' -> group_types t t'
193 | _,_ -> false
195 match l with
196 | [] | [_] -> l
197 | Ddecl (ty1, el1) :: Ddecl (ty2, el2) :: l'
198 when group_types ty1 ty2
199 -> group_defs_by_type (Ddecl(ty1, el1@el2)::l')
200 | Ddecl (ty1, el1) :: Ddecl (ty2, el2) :: Ddecl (ty3, el3) :: l'
201 when group_types ty1 ty3
202 -> group_defs_by_type (Ddecl (ty1, el1@el3) :: Ddecl (ty2, el2) :: l')
203 | Ddecl (ty1, el1) :: Ddecl (ty2, el2) :: Ddecl (ty3, el3) :: l'
204 when group_types ty2 ty3
205 -> group_defs_by_type (Ddecl (ty1, el1) :: Ddecl (ty2, el2@el3) :: l')
206 | Ddecl (ty1, el1) :: l' ->
207 Ddecl (ty1, el1) :: group_defs_by_type l'
208 | _ -> l
210 let rec elim_empty_blocks = function
211 | Sblock ([], s) -> elim_empty_blocks s
212 | Sblock (d,s) -> Sblock (d, elim_empty_blocks s)
213 | Sseq (s1,s2) -> Sseq (elim_empty_blocks s1, elim_empty_blocks s2)
214 | Sif (c,t,e) -> Sif(c, elim_empty_blocks t, elim_empty_blocks e)
215 | Swhile (c,s) -> Swhile(c, elim_empty_blocks s)
216 | Sfor(e1,e2,e3,s) -> Sfor(e1,e2,e3,elim_empty_blocks s)
217 | Snop | Sbreak | Sexpr _ | Sreturn _ as s -> s
219 let rec elim_nop = function
220 | Sseq (s1,s2) ->
221 let s1 = elim_nop s1 in
222 let s2 = elim_nop s2 in
223 begin match s1,s2 with
224 | Snop, Snop -> Snop
225 | Snop, s | s, Snop -> s
226 | _ -> Sseq(s1,s2)
228 | Sblock (d,s) ->
229 let s = elim_nop s in
230 begin match d, s with
231 | [], Snop -> Snop
232 | _ -> Sblock(d,s)
234 | Sif (c,t,e) -> Sif(c, elim_nop t, elim_nop e)
235 | Swhile (c,s) -> Swhile (c, elim_nop s)
236 | Sfor(e1,e2,e3,s) -> Sfor(e1,e2,e3,elim_nop s)
237 | Snop | Sbreak | Sexpr _ | Sreturn _ as s -> s
239 let is_expr = function
240 | Sexpr _ -> true
241 | _ -> false
243 let rec simplify_expr (d,s) : expr =
244 match (d,elim_empty_blocks(elim_nop s)) with
245 | [], Sblock([],s) -> simplify_expr ([],s)
246 | [], Sexpr e -> e
247 | [], Sif(c,t,e) ->
248 Equestion (c, simplify_expr([],t), simplify_expr([],e))
249 | _ ->
250 (* FIXME : use a dedicated exception here.
251 See also the capture of [Invalid_argument] below *)
252 raise (Invalid_argument "simplify_expr")
254 let rec simplify_cond (cd, cs) =
255 match cd,elim_empty_blocks(elim_nop cs) with
256 | [], Sexpr c -> ([], Sexpr c)
257 | ([], Sif (c', t', e') as b) ->
258 let _,t' = simplify_cond ([], t') in
259 let _,e' = simplify_cond ([], e') in
260 if is_false e' && is_expr t'(* c' && t' *)
261 then
262 let t' = simplify_expr ([],t') in
263 [], Sexpr(Ebinop(Band, c', t'))
264 else if is_true e' && is_expr t' (* !c' || t' *)
265 then
266 let t' = simplify_expr ([],t') in
267 [], Sexpr(Ebinop(Bor,Eunop(Unot,c'),t'))
268 else if is_true t' && is_expr e' (* c' || e' *)
269 then
270 let e' = simplify_expr ([],e') in
271 [], Sexpr(Ebinop(Bor,c',e'))
272 else if is_false t' && is_expr e' (* !c' && e' *)
273 then
274 let e' = simplify_expr ([],e') in
275 [], Sexpr(Ebinop(Band,Eunop(Unot,c'),e'))
276 else b
277 | d,s -> d, s
279 (* Operator precedence, needed to compute which parentheses can be removed *)
281 let prec_unop = function
282 | Unot | Ustar | Uaddr | Upreincr | Upredecr -> 2
283 | Upostincr | Upostdecr -> 1
285 let prec_binop = function
286 | Band -> 11
287 | Bor -> 11 (* really 12, but this avoids Wparentheses *)
288 | Beq | Bne -> 7
289 | Bassign -> 14
290 | Blt | Ble | Bgt | Bge -> 6
292 let is_const_unop (u:unop) = match u with
293 | Unot -> true
294 | Ustar | Uaddr | Upreincr | Upostincr | Upredecr | Upostdecr -> false
296 let is_const_binop (b:binop) = match b with
297 | Band | Bor | Beq | Bne | Blt | Ble | Bgt | Bge -> true
298 | Bassign -> false
300 let rec is_const_expr = function
301 | Enothing -> true
302 | Eunop (u,e) -> is_const_unop u && is_const_expr e
303 | Ebinop (b,e1,e2) ->
304 is_const_binop b && is_const_expr e1 && is_const_expr e2
305 | Equestion (c,_,_) -> is_const_expr c
306 | Ecast (_,_) -> true
307 | Ecall (_,_) -> false
308 | Econst _ -> true
309 | Evar _ -> false
310 | Elikely e | Eunlikely e -> is_const_expr e
311 | Esize_expr _ -> false
312 | Esize_type _ -> true
313 | Eindex (_,_) | Edot (_,_) | Earrow (_,_) -> false
314 | Esyntax (_,_,_,_,_) -> false
315 | Esyntaxrename _ -> false
317 let rec get_const_expr (d,s) =
318 let fail () = raise (Unsupported "non-constant array size") in
319 if (d = [])
320 then match elim_empty_blocks (elim_nop s) with
321 | Sexpr e -> if is_const_expr e then e
322 else fail ()
323 | Sblock (d, s) -> get_const_expr (d,s)
324 | _ -> fail ()
325 else fail ()
327 let rec has_unboxed_struct = function
328 | Tstruct _ -> true
329 | Tmutable _ -> true
330 | Tunion (_, lidty) -> List.exists has_unboxed_struct (List.map snd lidty)
331 | Tsyntax (_, lty) -> List.exists has_unboxed_struct lty
332 | _ -> false
334 let rec has_array = function
335 | Tarray _ -> true
336 | Tmutable t -> has_array t
337 | Tsyntax (_,lty) -> List.exists has_array lty
338 | Tstruct (_, lsty) -> List.exists has_array (List.map snd lsty)
339 | Tunion (_,lidty) -> List.exists has_array (List.map snd lidty)
340 | _ -> false
342 let rec should_not_escape = function
343 | Tstruct _ -> true
344 | Tarray _ -> true
345 | Tmutable _ -> true
346 | Tunion (_, lidty) -> List.exists should_not_escape (List.map snd lidty)
347 | Tsyntax (_, lty) -> List.exists should_not_escape lty
348 | _ -> false
350 let left_assoc = function
351 | Beq | Bne | Blt | Ble | Bgt | Bge -> true
352 | Bassign -> false
353 | Band | Bor -> false (* avoids Wparentheses *)
355 let right_assoc = function
356 | Bassign -> true
357 | _ -> false
359 let e_map fe (e:expr) =
360 match e with
361 | Enothing -> Enothing
362 | Eunop (u,e) -> Eunop (u, fe e)
363 | Ebinop (b, e1, e2) -> Ebinop (b, fe e1, fe e2)
364 | Equestion (c, t, e) -> Equestion (fe c, fe t, fe e)
365 | Ecast (t,e) -> Ecast(t, fe e)
366 | Ecall (f,args) -> Ecall (fe f, List.map fe args)
367 | Econst c -> Econst c
368 | Evar v -> Evar v
369 | Elikely e -> Elikely (fe e)
370 | Eunlikely e -> Eunlikely (fe e)
371 | Esize_expr e -> Esize_expr (fe e)
372 | Esize_type t -> Esize_type t
373 | Eindex (a,i) -> Eindex (fe a, fe i)
374 | Edot (e,f) -> Edot (fe e, f)
375 | Earrow (e, f) -> Earrow (fe e, f)
376 | Esyntaxrename (s,args) -> Esyntaxrename (s, List.map fe args)
377 | Esyntax (s,rt,ta,args,pl) ->
378 Esyntax (s, rt, ta, List.map (fun (e, t) -> (fe e, t)) args, pl)
380 let s_map fd fs fe (s:stmt) = match s with
381 | Snop -> Snop
382 | Sexpr e -> Sexpr (fe e)
383 | Sblock (d,s) -> Sblock (List.map fd d, fs s)
384 | Sseq (s1,s2) -> Sseq (fs s1, fs s2)
385 | Sif (ce,ts,es) -> Sif (fe ce, fs ts, fs es)
386 | Swhile (c,b) -> Swhile (fe c, fs b)
387 | Sfor (e1,e2,e3,b) -> Sfor (fe e1, fe e2, fe e3, fs b)
388 | Sbreak -> Sbreak
389 | Sreturn e -> Sreturn (fe e)
393 type info = {
394 syntax : Printer.syntax_map;
395 literal : Printer.syntax_map; (*TODO handle literals*)
396 kn : Pdecl.known_map;
397 prec : (int list) Mid.t;
400 let debug_c_extraction = Debug.register_info_flag
401 ~desc:"C extraction"
402 "c_extraction"
403 let debug_c_no_error_msgs =
404 Debug.register_flag
405 ~desc:"Disable the printing of the error messages in the C extraction"
406 "c_no_error_msgs"
408 module Print = struct
410 open C
411 open Format
412 open Printer
413 open Pp
415 exception Unprinted of string
417 let c_keywords = ["auto"; "break"; "case"; "char"; "const"; "continue"; "default"; "do"; "double"; "else"; "enum"; "extern"; "float"; "for"; "goto"; "if"; "int"; "long"; "register"; "return"; "short"; "signed"; "sizeof"; "static"; "struct"; "switch"; "typedef"; "union"; "unsigned"; "void"; "volatile"; "while" ]
419 let () = assert (List.length c_keywords = 32)
421 let sanitizer = sanitizer char_to_lalpha char_to_alnumus
422 let sanitizer s = Strings.lowercase (sanitizer s)
423 let local_printer = ref None
424 let global_printer = ref None
425 let printers_initialized = ref false
427 let init_printers blacklist =
428 if not !printers_initialized
429 then begin
430 let bl = c_keywords@blacklist in
431 local_printer := Some (create_ident_printer bl ~sanitizer);
432 global_printer := Some (create_ident_printer bl ~sanitizer);
433 printers_initialized := true
436 let c_static_inline = create_attribute "extraction:c_static_inline"
437 (* prints the c inline keyword *)
439 let print_local_ident fmt id =
440 pp_print_string fmt (id_unique (Option.get !local_printer) id)
441 let print_global_ident fmt id =
442 pp_print_string fmt (id_unique (Option.get !global_printer) id)
444 let clear_local_printer () = Ident.forget_all (Option.get !local_printer)
446 let space_nolinebreak fmt () = pp_print_string fmt " "
448 let protect_on ?(boxed=false) x s =
449 if x then "@[<1>(" ^^ s ^^ ")@]"
450 else if not boxed then "@[" ^^ s ^^ "@]"
451 else s
453 let extract_stars ty =
454 let rec aux acc = function
455 | Tptr t -> aux (acc+1) t
456 | t -> (acc, t)
458 aux 0 ty
460 let char_escape c = match c with
461 | '\'' -> "\\'"
462 | _ -> Constant.default_escape c
464 let rec print_ty ?(paren=false) fmt = function
465 | Tvoid -> pp_print_string fmt "void"
466 | Tsyntax (s, tl) ->
467 syntax_arguments s (print_ty ~paren:false) fmt tl
468 | Tptr ty -> fprintf fmt "%a *" (print_ty ~paren:true) ty
469 (* should be handled in extract_stars *)
470 | Tarray (ty, Enothing) ->
471 fprintf fmt (protect_on paren "%a *")
472 (print_ty ~paren:true) ty
473 (* raise (Unprinted "printing array type with unknown size")*)
474 | Tarray (ty, expr) ->
475 fprintf fmt (protect_on paren "%a[%a]")
476 (print_ty ~paren:true) ty (print_expr ~prec:1) expr
477 | Tstruct (s,_) -> fprintf fmt "struct %s" s
478 | Tunion _ -> raise (Unprinted "unions")
479 | Tnamed id -> print_global_ident fmt id
480 | Tmutable ty -> print_ty ~paren fmt ty
481 | Tnosyntax -> raise (Unprinted "type without syntax")
483 and print_unop fmt = function
484 | Unot -> pp_print_string fmt "!"
485 | Ustar -> pp_print_string fmt "*"
486 | Uaddr -> pp_print_string fmt "&"
487 | Upreincr | Upostincr -> pp_print_string fmt "++"
488 | Upredecr | Upostdecr -> pp_print_string fmt "--"
490 and unop_postfix = function
491 | Upostincr | Upostdecr -> true
492 | _ -> false
494 and print_binop fmt = function
495 | Band -> pp_print_string fmt "&&"
496 | Bor -> pp_print_string fmt "||"
497 | Beq -> pp_print_string fmt "=="
498 | Bne -> pp_print_string fmt "!="
499 | Bassign -> pp_print_string fmt "="
500 | Blt -> pp_print_string fmt "<"
501 | Ble -> pp_print_string fmt "<="
502 | Bgt -> pp_print_string fmt ">"
503 | Bge -> pp_print_string fmt ">="
505 and print_expr ~prec fmt = function
506 (* invariant: 0 <= prec <= 15 *)
507 | Enothing -> ()
508 | Eunop(u,e) ->
509 let p = prec_unop u in
510 if unop_postfix u
511 then fprintf fmt (protect_on (prec < p) "%a%a")
512 (print_expr ~prec:(p-1)) e print_unop u
513 else fprintf fmt (protect_on (prec < p) "%a%a")
514 print_unop u (print_expr ~prec:(p-1)) e
515 | Ebinop(b,e1,e2) ->
516 let p = prec_binop b in
517 let pleft = if left_assoc b then p else p-1 in
518 let pright = if right_assoc b then p else p-1 in
519 fprintf fmt (protect_on (prec < p) "%a %a %a")
520 (print_expr ~prec:pleft) e1 print_binop b (print_expr ~prec:pright) e2
521 | Equestion(c,t,e) ->
522 fprintf fmt (protect_on (prec < 13) "%a ? %a : %a")
523 (print_expr ~prec:12) c
524 (print_expr ~prec:15) t
525 (print_expr ~prec:13) e
526 | Ecast(ty, e) ->
527 fprintf fmt (protect_on (prec < 2) "(%a)%a")
528 (print_ty ~paren:false) ty (print_expr ~prec:2) e
529 | Esyntaxrename (s, l) ->
530 (* call to function defined in the prelude *)
531 fprintf fmt (protect_on (prec < 1) "%s(%a)")
532 s (print_list comma (print_expr ~prec:15)) l
533 | Ecall (Evar id, l) ->
534 fprintf fmt (protect_on (prec < 1) "%a(%a)")
535 print_global_ident id (print_list comma (print_expr ~prec:15)) l
536 | Ecall ((Esyntax _ as e),l) ->
537 fprintf fmt (protect_on (prec < 1) "%a(%a)")
538 (print_expr ~prec:1) e (print_list comma (print_expr ~prec:15)) l
539 | Ecall _ -> raise (Unsupported "complex function expression")
540 | Econst c -> print_const fmt c
541 | Evar id -> print_local_ident fmt id
542 | Elikely e -> fprintf fmt
543 (protect_on (prec < 1) "__builtin_expect(%a,1)")
544 (print_expr ~prec:15) e
545 | Eunlikely e -> fprintf fmt
546 (protect_on (prec < 1) "__builtin_expect(%a,0)")
547 (print_expr ~prec:15) e
548 | Esize_expr e ->
549 fprintf fmt (protect_on (prec < 2) "sizeof(%a)") (print_expr ~prec:15) e
550 | Esize_type ty ->
551 fprintf fmt (protect_on (prec < 2) "sizeof(%a)")
552 (print_ty ~paren:false) ty
553 | Edot (e,s) ->
554 fprintf fmt (protect_on (prec < 1) "%a.%s")
555 (print_expr ~prec:1) e s
556 | Eindex (e1, e2) ->
557 fprintf fmt (protect_on (prec <= 1) "%a[%a]")
558 (print_expr ~prec:1) e1
559 (print_expr ~prec:15) e2
560 | Earrow (e,s) ->
561 fprintf fmt (protect_on (prec < 1) "%a->%s")
562 (print_expr ~prec:1) e s
563 | Esyntax ("%1", _, _, [e,_], _) ->
564 print_expr ~prec fmt e
565 | Esyntax (s, t, args, lte, pl) ->
566 let s =
567 if pl = [] || prec < List.hd pl
568 then Format.sprintf "(%s)" s
569 else s in
570 let lte = Array.of_list lte in
571 let pr fmt p c i =
572 match c with
573 | None -> print_expr ~prec:p fmt (fst lte.(i - 1))
574 | Some 't' ->
575 let v = if i = 0 then t else snd lte.(i - 1) in
576 print_ty ~paren:false fmt v
577 | Some 'v' ->
578 print_ty ~paren:false fmt args.(i)
579 | Some 'd' -> (* dereference the value *)
580 begin match fst lte.(i - 1) with
581 | Eunop (Uaddr, e) -> print_expr ~prec:p fmt e
582 | e -> print_expr ~prec:p fmt (Eunop (Ustar, e))
584 | Some c -> raise (BadSyntaxKind c) in
585 gen_syntax_arguments_prec fmt s pr pl
587 and print_const fmt = function
588 | Cint (s,_) | Cfloat s -> pp_print_string fmt s
589 | Cchar s -> fprintf fmt "'%s'" Constant.(escape char_escape s)
590 | Cstring s -> fprintf fmt "\"%s\"" Constant.(escape default_escape s)
591 let print_id_init ?(size=None) ~stars fmt ie =
592 (if stars > 0
593 then fprintf fmt "%s " (String.make stars '*')
594 else ());
595 match size, ie with
596 | None, (id, Enothing) -> print_local_ident fmt id
597 | None, (id,e) ->
598 fprintf fmt "%a = %a"
599 print_local_ident id (print_expr ~prec:(prec_binop Bassign)) e
600 | Some Enothing, (id, Enothing) ->
601 (* size unknown, treat as pointer *)
602 fprintf fmt "* %a" print_local_ident id
603 | Some e, (id, Enothing) ->
604 fprintf fmt "%a[%a]" print_local_ident id (print_expr ~prec:15) e
605 | Some _es, (_id, _ei) -> raise (Unsupported "array initializer")
607 let print_expr_no_paren fmt expr = print_expr ~prec:max_int fmt expr
609 let rec print_stmt ~braces fmt = function
610 | Snop | Sexpr Enothing -> Debug.dprintf debug_c_extraction "snop"; ()
611 | Sexpr e -> fprintf fmt "%a;" print_expr_no_paren e;
612 | Sblock ([] ,s) when not braces ->
613 (print_stmt ~braces:false) fmt s
614 | Sblock b ->
615 fprintf fmt "@[<hov>{@\n @[<hov>%a@]@\n}@]" print_body b
616 | Sseq (s1,s2) -> fprintf fmt "%a@\n%a"
617 (print_stmt ~braces:false) s1
618 (print_stmt ~braces:false) s2
619 | Sif(c,t,e) when is_nop e ->
620 fprintf fmt "@[<hov 2>if (%a) {@\n@[<hov>%a@]@]@\n}"
621 print_expr_no_paren c (print_stmt ~braces:false) t
622 | Sif(c,t,e) ->
623 fprintf fmt
624 "@[<hov 2>if (%a) {@\n@[<hov>%a@]@]@\n@[<hov 2>} else {@\n@[<hov>%a@]@]@\n}"
625 print_expr_no_paren c
626 (print_stmt ~braces:false) t
627 (print_stmt ~braces:false) e
628 | Swhile (e,b) -> fprintf fmt "@[<hov 2>while (%a) {@\n@[<hov>%a@]@]@\n}"
629 print_expr_no_paren e (print_stmt ~braces:false) b
630 | Sfor (einit, etest, eincr, s) ->
631 fprintf fmt "@[<hov 2>for (%a; %a; %a) {@\n@[<hov>%a@]@]@\n}"
632 print_expr_no_paren einit
633 print_expr_no_paren etest
634 print_expr_no_paren eincr
635 (print_stmt ~braces:false) s
636 | Sbreak -> pp_print_string fmt "break;"
637 | Sreturn Enothing -> pp_print_string fmt "return;"
638 | Sreturn e -> fprintf fmt "return %a;" print_expr_no_paren e
640 and print_def ~global fmt def =
641 let print_inline fmt id =
642 if Sattr.mem c_static_inline id.id_attrs
643 then pp_print_string fmt "static inline " in
644 match def with
645 | Dfun (id,(rt,args),body) ->
646 fprintf fmt "@[@\n@[<hv 2>%a%a %a(@[%a@]) {@\n@[%a@]@]\n}@]"
647 print_inline id
648 (print_ty ~paren:false) rt
649 print_global_ident id
650 (print_list comma
651 (print_pair_delim nothing space_nolinebreak nothing
652 (print_ty ~paren:false) print_local_ident))
653 args
654 print_body body
655 | Dproto (id, (rt, args)) ->
656 fprintf fmt "@\n%a %a(@[%a@]);"
657 (print_ty ~paren:false) rt
658 print_global_ident id
659 (print_list comma
660 (print_pair_delim nothing space_nolinebreak nothing
661 (print_ty ~paren:false) print_local_ident))
662 args
663 | Ddecl (Tarray(ty, e), lie) ->
664 fprintf fmt "%a @[<hov>%a@];"
665 (print_ty ~paren:false) ty
666 (print_list comma (print_id_init ~stars:0 ~size:(Some e)))
668 | Ddecl (ty, lie) ->
669 if global then pp_force_newline fmt ();
670 let nb, ty = extract_stars ty in
671 assert (nb = 0);
672 fprintf fmt "%a @[<hov>%a@];"
673 (print_ty ~paren:false) ty
674 (print_list comma (print_id_init ~stars:nb ~size:None))
676 | Dextern (ty, id) ->
677 fprintf fmt "@\nextern %a %a;" (print_ty ~paren:false) ty print_local_ident id
678 | Dstruct (s, lf) ->
679 fprintf fmt "@\nstruct %s {@\n%a};" s
680 (print_list_suf newline
681 (fun fmt (s,ty) -> fprintf fmt " %a %s;"
682 (print_ty ~paren:false) ty s))
684 | Dstruct_decl s ->
685 fprintf fmt "struct %s;@;" s
686 | Dinclude (id, Sys) ->
687 fprintf fmt "#include <%s.h>" (sanitizer id.id_string)
688 | Dinclude (id, Proj) ->
689 fprintf fmt "#include \"%s.h\"" (sanitizer id.id_string)
690 | Dtypedef (ty,id) ->
691 fprintf fmt "@[<hov>typedef@ %a@;%a;@]"
692 (print_ty ~paren:false) ty print_global_ident id
694 and print_body fmt (def, s) =
695 if def = []
696 then print_stmt ~braces:true fmt s
697 else
698 print_pair_delim nothing newline nothing
699 (print_list newline (print_def ~global:false))
700 (print_stmt ~braces:true)
701 fmt (def,s)
703 let print_global_def fmt def =
705 clear_local_printer ();
706 let buf = Buffer.create 1024 in
707 let f = formatter_of_buffer buf in
708 print_def ~global:true f def;
709 pp_print_flush f ();
710 pp_print_string fmt (Buffer.contents buf)
711 with
712 Unprinted s ->
713 if Debug.test_noflag debug_c_no_error_msgs
714 then
715 Format.eprintf
716 "Could not print declaration of %s. Unsupported: %s@."
717 !current_decl_name s
721 module MLToC = struct
723 open Ity
724 open Ty
725 open Expr
726 open Term
727 open Printer
728 open Mltree
729 open C
731 let field i = "__field_"^(string_of_int i)
733 let structs : struct_def Hid.t = Hid.create 16
734 let aliases : C.ty Hid.t = Hid.create 16
735 let globals : unit Hid.t = Hid.create 16
737 let array = create_attribute "extraction:array"
738 let array_mk = create_attribute "extraction:array_make"
739 let likely = create_attribute "extraction:likely"
740 let unlikely = create_attribute "extraction:unlikely"
742 let decl_attribute = create_attribute "extraction:c_declaration"
743 let rec ty_of_mlty info = function
744 | Tvar { tv_name = id } ->
745 begin match query_syntax info.syntax id
746 with
747 | Some s -> C.Tsyntax (s, [])
748 | None -> C.Tnamed id
750 | Tapp (id, [t]) when Sattr.mem array id.id_attrs ->
751 Tarray (ty_of_mlty info t, Enothing)
752 | Tapp (id, [t]) when id_equal id Pmodule.ts_ref.ts_name ->
753 Tmutable (ty_of_mlty info t)
754 | Tapp (id, tl) ->
755 begin match query_syntax info.syntax id
756 with
757 | Some s -> C.Tsyntax (s, List.map (ty_of_mlty info) tl)
758 | None ->
759 if tl = []
760 then if Hid.mem aliases id
761 then Hid.find aliases id
762 else try Tstruct (Hid.find structs id)
763 with Not_found -> Tnosyntax
764 else Tnosyntax
766 | Ttuple [] -> C.Tvoid
767 | Ttuple [t] -> ty_of_mlty info t
768 | Ttuple _ -> raise (Unsupported "tuple parameters")
769 | Tarrow _ -> raise (Unsupported "arrow type")
771 let struct_of_rs info mlty rs : struct_def =
772 let s = match query_syntax info.syntax rs.rs_name with
773 | Some s -> s
774 | None -> rs.rs_name.id_string in
775 let name = Pp.sprintf "__%s_result" s in
776 let fields =
777 match mlty with
778 | Ttuple lt ->
779 let rec fields fr tys = match tys with
780 | [] -> []
781 | ty::l -> (field fr, ty_of_mlty info ty)::(fields (fr+1) l) in
782 fields 0 lt
783 | _ -> assert false
785 (name, fields)
787 let struct_of_rs info mlty =
788 Wrs.memoize 17 (fun rs -> struct_of_rs info mlty rs)
790 let ity_of_expr e = match e.e_ity with
791 | I i -> i
792 | _ -> assert false
794 let pv_name pv = pv.pv_vs.vs_name
796 let is_constructor rs its =
797 List.exists (rs_equal rs) its.Pdecl.itd_constructors
799 let is_struct_constructor info rs =
800 let open Pdecl in
801 if is_rs_tuple rs then false
802 else match Mid.find_opt rs.rs_name info.kn with
803 | Some { pd_node = PDtype its } ->
804 List.exists (is_constructor rs) its
805 | _ -> false
807 let struct_of_constructor info rs =
808 let open Pdecl in
809 match Mid.find rs.rs_name info.kn with
810 | { pd_node = PDtype its } ->
811 let its = List.find (is_constructor rs) its in
812 let id = its.itd_its.its_ts.ts_name in
813 (try Hid.find structs id
814 with Not_found -> raise (Unsupported "bad struct"))
815 | _ -> assert false
817 type syntax_env = { in_unguarded_loop : bool;
818 computes_return_value : bool;
819 current_function : rsymbol;
820 ret_regs : Sreg.t;
821 breaks : Sid.t;
822 returns : Sid.t;
823 array_sizes : C.expr Mid.t;
824 boxed : unit Hreg.t;
825 (* is this struct boxed or passed by value? *)
828 let is_unit = function
829 | Ttuple [] -> true
830 | _ -> false
832 let handle_likely attrs (e:C.expr) =
833 let lkl = Sattr.mem likely attrs in
834 let ulk = Sattr.mem unlikely attrs in
835 if lkl
836 then (if ulk then e else Elikely e)
837 else (if ulk then Eunlikely e else e)
839 let reverse_likely = function
840 | Elikely e -> Eunlikely e
841 | Eunlikely e -> Elikely e
842 | e -> e
844 let lit_one = Number.(int_literal ILitDec ~neg:false "1")
845 let lit_zero = Number.(int_literal ILitDec ~neg:false "0")
847 (* /!\ Do not use if e may have type unit and not be Enothing *)
848 let expr_or_return env e =
849 if env.computes_return_value
850 then Sreturn e
851 else Sexpr e
853 let ity_escapes_from_expr env ity e =
854 let aregs = ity_exp_fold Sreg.add_left Sreg.empty ity in
855 let reset_regs = e.e_effect.eff_resets in
856 let locked_regs = Sreg.union env.ret_regs reset_regs in
857 not (Sreg.is_empty (Sreg.inter aregs locked_regs))
859 let var_escapes_from_expr env v e =
860 ity_escapes_from_expr env v.pv_ity e
862 let rec expr info env (e:Mltree.expr) : C.body =
863 let do_let id ity cty le e =
864 if should_not_escape cty && ity_escapes_from_expr env ity e
865 then raise (Unsupported "array or struct escaping function");
866 let (d,s) = expr info {env with computes_return_value = false} le in
867 let initblock = d, C.assignify (Evar id) s in
868 [ C.Ddecl (cty, [id, C.Enothing]) ],
869 C.Sseq (C.Sblock initblock, C.Sblock (expr info env e)) in
870 let do_for (eb: pvsymbol) (ee: Mltree.expr option)
871 (sb: pvsymbol) (se: Mltree.expr option) i imlty dir body =
872 let open Number in
873 match i.pv_vs.vs_ty.ty_node with
874 | Tyapp ({ ts_def = Range { ir_lower = lb; ir_upper = ub }},_) ->
875 let init_test_ok, end_test_ok =
876 match se, ee with
877 | _, Some { e_node = Mltree.Econst (Constant.ConstInt ec) } ->
878 true,
879 if dir = To
880 then BigInt.lt ec.il_int ub
881 else BigInt.lt lb ec.il_int
882 | Some { e_node = Mltree.Econst (Constant.ConstInt sc) }, _ ->
883 (if dir = To
884 then BigInt.eq sc.il_int lb
885 else BigInt.eq sc.il_int ub),
886 false
887 | _, _ -> false, false
889 let ty = ty_of_mlty info imlty in
890 let di = C.Ddecl(ty, [i.pv_vs.vs_name, Enothing]) in
891 let ei = C.Evar (i.pv_vs.vs_name) in
892 let env_f = { env with computes_return_value = false } in
893 let ds, is, ie = match se with
894 | Some se ->
895 let ds, is = expr info env_f se in
896 begin match is with
897 | Sexpr (Econst _ | Evar _ as e) ->
898 ds, Snop, e
899 | _ ->
900 let iv = Evar (pv_name sb) in
901 let is = assignify iv is in
902 C.Ddecl (ty, [pv_name sb, Enothing]) :: ds, is, iv
904 | None -> [], Snop, Evar(pv_name sb)
906 let init_e = C.Ebinop (Bassign, ei, ie) in
907 let de, es, ee =
908 match ee with
909 | Some ee ->
910 let de, es = expr info env_f ee in
911 begin match es with
912 | Sexpr (Econst _ | Evar _ as e) ->
913 de, Snop, e
914 | _ ->
915 let ev = Evar (pv_name eb) in
916 let es = assignify ev es in
917 C.Ddecl (ty, [pv_name eb, Enothing]) :: de, es, ev
919 | None -> [], Snop, Evar (pv_name eb)
921 let d = di :: ds @ de in
922 let incr_op = match dir with To -> Upreincr | DownTo -> Upredecr in
923 let incr_e = C.Eunop (incr_op, ei) in
924 let env' = { env with computes_return_value = false;
925 in_unguarded_loop = true;
926 breaks =
927 if env.in_unguarded_loop
928 then Sid.empty else env.breaks } in
929 let bd, bs = expr info env' body in
930 let test_op = match dir with | To -> C.Ble | DownTo -> C.Bge in
931 let sloop =
932 if end_test_ok
933 then C.Sfor(init_e, C.Ebinop (test_op, ei, ee),
934 incr_e, C.Sblock(bd, bs))
935 else
936 let end_test = C.Sif (C.Ebinop (C.Beq, ei, ee),
937 Sbreak, Snop) in
938 let bs = C.Sseq (bs, end_test) in
939 C.Sfor(init_e, Enothing, incr_e, C.Sblock(bd,bs)) in
940 let ise = C.Sseq (is, es) in
941 let s = if init_test_ok
942 then sloop
943 else C.(Sif (Ebinop (test_op, ie, ee), sloop, Snop)) in
944 d, C.Sseq (ise, s)
945 | _ ->
946 raise (Unsupported "for loops where loop index is not a range type")
948 match e.e_node with
949 | Eblock [] -> ([], expr_or_return env Enothing)
950 | Eblock [e] -> [], C.Sblock (expr info env e)
951 | Eblock l ->
952 let env_f = { env with computes_return_value = false } in
953 let rec aux = function
954 | [] -> ([], expr_or_return env Enothing)
955 | [s] -> ([], C.Sblock (expr info env s))
956 | h::t -> ([], C.Sseq (C.Sblock (expr info env_f h),
957 C.Sblock (aux t)))
959 aux l
960 | Eapp (_, _, true) -> raise (Unsupported "partial application")
961 | Eapp (rs, [], false) when rs_equal rs rs_true ->
962 Debug.dprintf debug_c_extraction "true@.";
963 ([],expr_or_return env (C.Econst (Cint ("1",lit_one))))
964 | Eapp (rs, [], false) when rs_equal rs rs_false ->
965 Debug.dprintf debug_c_extraction "false@.";
966 ([],expr_or_return env (C.Econst (Cint ("0", lit_zero))))
967 | Mltree.Evar pv ->
968 let id = pv_name pv in
969 let e = C.Evar id in
970 ([], expr_or_return env e)
971 | Mltree.Econst (Constant.ConstStr s) ->
972 C.([], expr_or_return env (Econst (Cstring s)))
973 | Mltree.Econst (Constant.ConstReal _) ->
974 raise (Unsupported "real constant")
975 | Mltree.Econst (Constant.ConstInt ic) ->
976 let open Number in
977 let print fmt ic =
978 let n = ic.il_int in
979 if BigInt.lt n BigInt.zero
980 then
981 (* default to base 10 for negative literals *)
982 Format.fprintf fmt "-%a" (print_in_base 10 None) (BigInt.abs n)
983 else
984 match ic.il_kind with
985 | ILitHex | ILitBin
986 -> Format.fprintf fmt "0x%a" (print_in_base 16 None) n
987 | ILitOct -> Format.fprintf fmt "0%a" (print_in_base 8 None) n
988 | ILitDec | ILitUnk ->
989 (* default to base 10 *)
990 print_in_base 10 None fmt n in
991 let s =
992 let tname = match e.e_mlty with
993 | Tapp (id, _) -> id
994 | _ -> assert false in
995 begin match query_syntax info.literal tname with
996 | Some st ->
997 Format.asprintf "%a" (syntax_range_literal ~cb:(Some print) st) ic
998 | _ ->
999 let s = tname.id_string in
1000 raise (Unsupported ("unspecified number format for type "^s)) end
1002 let e = C.(Econst (Cint (s, ic))) in
1003 ([], expr_or_return env e)
1004 | Eapp (rs, [], _) when Hid.mem globals rs.rs_name ->
1005 Debug.dprintf debug_c_extraction "global variable %s@."
1006 rs.rs_name.id_string;
1007 [], expr_or_return env (Evar rs.rs_name)
1008 | Eapp (rs, _, _) when Sattr.mem decl_attribute rs.rs_name.id_attrs ->
1009 raise (Unsupported "local variable declaration call outside let-in")
1010 | Eapp (rs, [e], _) when rs_equal rs Pmodule.rs_ref ->
1011 Debug.dprintf debug_c_extraction "ref constructor@.";
1012 let env_f = { env with computes_return_value = false } in
1013 let arg = simplify_expr (expr info env_f e) in
1014 ([], expr_or_return env arg)
1015 | Eapp (rs, el, _)
1016 when is_struct_constructor info rs
1017 && query_syntax info.syntax rs.rs_name = None ->
1018 Debug.dprintf debug_c_extraction "constructor %s@." rs.rs_name.id_string;
1019 let args =
1020 List.filter
1021 (fun e ->
1022 assert (not e.e_effect.eff_ghost);
1023 (not (Sattr.mem dummy_expr_attr e.e_attrs)) &&
1024 (not (is_unit e.e_mlty)))
1025 el in
1026 let env_f = { env with computes_return_value = false } in
1027 let args = List.map (fun e -> simplify_expr (expr info env_f e)) args in
1028 let ((sname, sfields) as sd) = struct_of_constructor info rs in
1029 let id = id_register (id_fresh sname) in
1030 let defs = [ Ddecl (Tstruct sd, [id, Enothing]) ] in
1031 let st = Evar id in
1032 let assign_expr f arg = Sexpr (Ebinop (Bassign, Edot (st, f), arg)) in
1033 let inits =
1034 List.fold_left2
1035 (fun acc (f, _ty) arg -> Sseq (acc,assign_expr f arg))
1036 Snop sfields args in
1037 (defs, Sseq (inits, expr_or_return env st))
1038 | Eapp (rs, el, _) ->
1039 Debug.dprintf debug_c_extraction "call to %s@." rs.rs_name.id_string;
1040 let args = List.filter (fun e -> not (is_unit e.e_mlty)) el
1041 in (*FIXME still needed with masks? *)
1042 let env_f = { env with computes_return_value = false } in
1043 if is_rs_tuple rs
1044 then begin
1045 match args with
1046 | [] -> C.([], expr_or_return env Enothing);
1047 | [e] -> expr info env e
1048 | _ ->
1049 let id_struct = id_register (result_id ()) in
1050 let e_struct = C.Evar id_struct in
1051 let d_struct =
1052 C.(Ddecl(Tstruct
1053 (struct_of_rs info e.e_mlty env.current_function),
1054 [id_struct, Enothing])) in
1055 let assign i (d,s) =
1056 C.Sblock (d,assignify C.(Edot (e_struct, field i)) s) in
1057 let rec assigns args i =
1058 match args with
1059 | [] -> Snop
1060 | e::t ->
1061 let b = expr info env_f e in
1062 C.Sseq(assign i b, assigns t (i+1)) in
1063 C.([d_struct], Sseq(assigns args 0, expr_or_return env e_struct))
1065 else
1066 let (prdefs, prstmt), e' =
1067 let prelude, unboxed_params =
1068 Lists.map_fold_left
1069 (fun ((accd, accs) as acc) e ->
1070 let pty = ty_of_mlty info e.e_mlty in
1071 let d, s = expr info env_f e in
1073 acc,
1074 (simplify_expr (d,s), pty)
1075 with
1076 (* FIXME: use a dedicated exception instead.
1077 see also [simplify_expr]. *)
1078 Invalid_argument "simplify_expr" ->
1079 let s', e' = get_last_expr s in
1080 (accd@d, Sseq(accs, s')), (e', pty))
1081 ([], Snop)
1082 args in
1083 let params =
1084 List.map2
1085 (fun p mle ->
1086 match (p, mle) with
1087 | (ce, Tstruct s), { e_ity = I { ity_node = Ityreg r }}
1088 when not (Hreg.mem env.boxed r) ->
1089 C.(Eunop(Uaddr, ce), Tptr (Tstruct s))
1090 | (ce, Tmutable t), { e_ity = I { ity_node = Ityreg r }}
1091 when not (Hreg.mem env.boxed r) ->
1092 (Eunop(Uaddr, ce), Tptr t)
1093 | p, _ -> p)
1094 unboxed_params args in
1095 prelude,
1096 match query_syntax info.syntax rs.rs_name with
1097 | Some s ->
1098 let complex s =
1099 String.contains s '%'
1100 || String.contains s ' '
1101 || String.contains s '(' in
1102 let p = Mid.find rs.rs_name info.prec in
1103 if complex s
1104 then
1105 let mlty = e.e_mlty in
1106 let args = match mlty with
1107 | Tvar _ -> [||]
1108 | Tapp (_, args) | Ttuple args->
1109 Array.of_list (List.map (ty_of_mlty info) args)
1110 | _ -> assert false
1112 C.Esyntax(s,ty_of_mlty info mlty, args, params, p)
1113 else
1114 if args=[]
1115 then C.(Esyntax(s, Tnosyntax, [||], [], p)) (*constant*)
1116 else
1117 (*function defined in the prelude *)
1118 let cargs = List.map fst params in
1119 C.(Esyntaxrename (s, cargs))
1120 | None ->
1121 match rs.rs_field with
1122 | None ->
1123 C.(Ecall(Evar(rs.rs_name), List.map fst params))
1124 | Some pv ->
1125 assert (List.length el = 1);
1126 begin match unboxed_params, args with
1127 | [(ce, Tstruct _)], [{ e_ity = I { ity_node = Ityreg r }}] ->
1128 if Hreg.mem env.boxed r
1129 then C.Earrow (ce, (pv_name pv).id_string)
1130 else C.Edot (ce, (pv_name pv).id_string)
1131 | [(ce, Tmutable _)], [{ e_ity = I { ity_node = Ityreg r }}] ->
1132 if Hreg.mem env.boxed r then C.Eunop (C.Ustar, ce) else ce
1133 | _ -> C.Edot (fst (List.hd params), (pv_name pv).id_string)
1136 let s =
1137 if env.computes_return_value
1138 then
1139 if is_unit e.e_mlty
1140 then Sseq(Sexpr e', Sreturn Enothing)
1141 else Sreturn e'
1142 else Sexpr e' in
1143 if is_nop prstmt
1144 then prdefs, s
1145 else C.(prdefs, Sseq(prstmt, s))
1146 | Eif (cond, th, el) ->
1147 let cd,cs = expr info {env with computes_return_value = false} cond in
1148 let t = expr info env th in
1149 let e = expr info env el in
1150 begin match simplify_cond (cd, cs) with
1151 | [], C.Sexpr c ->
1152 let c = handle_likely cond.e_attrs c in
1153 if Mltree.is_false th && Mltree.is_true el
1154 then C.([], expr_or_return env (Eunop(Unot, c)))
1155 else [], C.Sif(c,C.Sblock t, C.Sblock e)
1156 | cdef, cs ->
1157 let cid = id_register (id_fresh "cond") in (* ? *)
1158 C.Ddecl (C.Tsyntax ("int",[]), [cid, C.Enothing])::cdef,
1159 C.Sseq (C.assignify (Evar cid) cs,
1160 C.Sif ((handle_likely cond.e_attrs (C.Evar cid)),
1161 C.Sblock t, C.Sblock e))
1163 | Ewhile (c,b) ->
1164 Debug.dprintf debug_c_extraction "while@.";
1165 let cd, cs = expr info {env with computes_return_value = false} c in
1166 (* this is needed so that the extracted expression has all
1167 needed variables in its scope *)
1168 let cd, cs = C.flatten_defs cd cs in
1169 let cd = C.group_defs_by_type cd in
1170 let env' = { env with
1171 computes_return_value = false;
1172 in_unguarded_loop = true;
1173 breaks =
1174 if env.in_unguarded_loop
1175 then Sid.empty else env.breaks } in
1176 let b = expr info env' b in
1177 begin match (C.simplify_cond (cd,cs)) with
1178 | cd, C.Sexpr ce -> cd, C.Swhile (handle_likely c.e_attrs ce, C.Sblock b)
1179 | _ ->
1180 begin match C.get_last_expr cs with
1181 | C.Snop, e -> cd, C.(Swhile(handle_likely c.e_attrs e, C.Sblock b))
1182 | s,e ->
1183 let brc = reverse_likely (handle_likely c.e_attrs (Eunop(Unot,e))) in
1184 cd, C.(Swhile(Econst (Cint ("1", lit_one)),
1185 Sseq(s,
1186 Sseq(Sif(brc, Sbreak, Snop),
1187 C.Sblock b))))
1190 | Ematch (b, [], (_::_ as xl)) ->
1191 Debug.dprintf debug_c_extraction "TRY@.";
1192 let is_while = match b.e_node with Ewhile _ -> true | _-> false in
1193 let breaks, returns = List.fold_left
1194 (fun (bs,rs) (xs, pvsl, r) ->
1195 let id = xs.xs_name in
1196 match pvsl, r.e_node with
1197 | [], (Eblock []) when is_while ->
1198 assert (is_unit r.e_mlty);
1199 (Sid.add id bs, rs)
1200 | [pv], Mltree.Evar pv'
1201 when pv_equal pv pv' && env.computes_return_value ->
1202 (bs, Sid.add id rs)
1203 | [], Mltree.Eblock [] when env.computes_return_value ->
1204 assert (is_unit r.e_mlty);
1205 (bs, Sid.add id rs)
1206 | _ -> raise (Unsupported "non break/return exception in try"))
1207 (Sid.empty, env.returns) xl
1209 let env' = { env with
1210 in_unguarded_loop = false;
1211 breaks = breaks;
1212 returns = returns;
1213 boxed = env.boxed;
1214 } in
1215 expr info env' b
1216 | Eraise (xs,_) when Sid.mem xs.xs_name env.breaks ->
1217 Debug.dprintf debug_c_extraction "BREAK@.";
1218 ([], C.Sbreak)
1219 | Eraise (xs, Some r) when Sid.mem xs.xs_name env.returns ->
1220 Debug.dprintf debug_c_extraction "RETURN@.";
1221 expr info {env with computes_return_value = true} r
1222 | Eraise (xs, None)
1223 when Sid.mem xs.xs_name env.returns &&
1224 ity_equal Ity.ity_unit env.current_function.rs_cty.cty_result
1225 -> ([], C.Sreturn Enothing)
1226 | Eraise (xs, None) when Sid.mem xs.xs_name env.returns ->
1227 assert false (* nothing to pass to return *)
1228 | Eraise _ -> raise (Unsupported "non break/return exception raised")
1229 | Elet (Lvar(eb, ee),
1230 { e_node = Elet(Lvar (sb, se),
1231 { e_node = Efor (i, ty, sb', dir, eb', body) })})
1232 when pv_equal sb sb' && pv_equal eb eb' ->
1233 Debug.dprintf debug_c_extraction "LETLETFOR@.";
1234 do_for eb (Some ee) sb (Some se) i ty dir body
1235 | Elet (Lvar (eb, ee), { e_node = Efor (i, ty, sb, dir, eb', body) })
1236 when pv_equal eb eb' ->
1237 Debug.dprintf debug_c_extraction "ENDLETFOR@.";
1238 do_for eb (Some ee) sb None i ty dir body
1239 | Elet (Lvar (sb, se), { e_node = Efor (i, ty, sb', dir, eb, body) })
1240 when pv_equal sb sb' ->
1241 Debug.dprintf debug_c_extraction "STARTLETFOR@.";
1242 do_for eb None sb (Some se) i ty dir body
1243 | Efor (i, ty, sb, dir, eb, body) ->
1244 Debug.dprintf debug_c_extraction "FOR@.";
1245 do_for eb None sb None i ty dir body
1246 | Ematch (({e_node = Eapp(_rs,_, _)} as _e1), [Pwild, _e2], []) ->
1247 assert false (* simplified at Compile *)
1248 | Ematch (_e1, [Pvar _v, _e2], []) ->
1249 assert false (* simplified at Compile *)
1250 | Ematch (({e_node = Eapp(rs,_, _)} as e1), [Ptuple rets,e2], [])
1251 | Ematch ({e_node = Eblock [{e_node = Eapp(rs, _, _)} as e1]}, [Ptuple rets, e2], [])
1252 when List.for_all
1253 (function | Pwild (*ghost*) | Pvar _ -> true |_-> false)
1254 rets
1256 begin match rets with
1257 | _ ->
1258 let id_struct = id_register (id_fresh "struct_res") in
1259 let e_struct = C.Evar id_struct in
1260 let d_struct = C.Ddecl(C.Tstruct (struct_of_rs info e1.e_mlty rs),
1261 [id_struct, C.Enothing]) in
1262 let defs =
1263 match e1.e_mlty with
1264 | Ttuple lt ->
1265 List.fold_right2
1266 (fun p ty acc ->
1267 match p with
1268 | Pvar vs -> C.Ddecl(ty_of_mlty info ty,
1269 [vs.vs_name, C.Enothing])::acc
1270 | Pwild -> acc
1271 | _ -> assert false )
1272 rets lt [d_struct]
1273 | _ -> assert false in
1274 let d,s = expr info {env with computes_return_value = false} e1 in
1275 let s = assignify e_struct s in
1276 let assign vs i =
1277 assignify (C.Evar vs) C.(Sexpr (Edot (e_struct, field i))) in
1278 let rec assigns rets i =
1279 match rets with
1280 | [] -> C.Snop
1281 | Pvar vs :: t -> C.Sseq ((assign vs.vs_name i), (assigns t (i+1)))
1282 | Pwild :: t -> assigns t (i+1) (* ghost variable, skip *)
1283 | _ -> assert false in
1284 let assigns = assigns rets 0 in
1285 let b = expr info env e2 in
1286 d@defs, C.(Sseq(Sseq(s,assigns), Sblock b)) end
1287 | Ematch _ -> raise (Unsupported "pattern matching")
1288 | Eabsurd -> assert false
1289 | Eassign ([e1, pvmlty, ({rs_field = Some _} as rs), e2]) ->
1290 let boxed =
1291 match (ity_of_expr e1).ity_node with
1292 | Ityreg r -> Hreg.mem env.boxed r
1293 | _ -> false in
1294 let ce1 = simplify_expr
1295 (expr info { env with computes_return_value = false } e1) in
1296 let t =
1297 match query_syntax info.syntax rs.rs_name with
1298 | Some s ->
1299 let _ =
1301 Re.Str.search_forward
1302 (Re.Str.regexp "[%]\\([tv]?\\)[0-9]+") s 0
1303 with Not_found -> raise (Unsupported "assign field format") in
1304 let st = if boxed
1305 then C.(Eunop(Ustar, ce1))
1306 else ce1 in
1307 let params = [ st, ty_of_mlty info pvmlty ] in
1308 let args = match pvmlty with
1309 | Tvar _ -> [||]
1310 | Tapp (_,args) | Ttuple args ->
1311 Array.of_list (List.map (ty_of_mlty info) args)
1312 | _ -> assert false
1314 let p = Mid.find rs.rs_name info.prec in
1315 C.Esyntax(s,ty_of_mlty info pvmlty, args, params,p)
1316 | None ->
1317 let ty = ty_of_mlty info pvmlty in
1318 match ty with
1319 | Tmutable _ ->
1320 if boxed then Eunop (Ustar, ce1) else ce1
1321 | _ ->
1322 if boxed
1323 then Earrow (ce1, rs.rs_name.id_string)
1324 else Edot (ce1, rs.rs_name.id_string) in
1325 let d,v = expr info { env with computes_return_value = false } e2 in
1326 d, C.(Sexpr(Ebinop(Bassign, t, simplify_expr ([],v))))
1327 | Eassign _ -> raise (Unsupported "assign")
1328 | Eexn (_,_,e) -> expr info env e
1329 | Eignore e ->
1330 Debug.dprintf debug_c_extraction "Eignore@.";
1332 C.Sseq(C.Sblock(expr info {env with computes_return_value = false} e),
1333 if env.computes_return_value
1334 then C.Sreturn(Enothing)
1335 else C.Snop)
1336 | Efun _ -> raise (Unsupported "higher order")
1337 | Elet (Lvar (v, ({ e_node = Eapp (rs, al, _) } as ev)), e)
1338 when Sattr.mem decl_attribute rs.rs_name.id_attrs
1339 && query_syntax info.syntax rs.rs_name <> None
1340 && List.for_all (fun e -> is_unit e.e_mlty) al
1342 Debug.dprintf debug_c_extraction "variable declaration call@.";
1343 if var_escapes_from_expr env v e
1344 then raise (Unsupported "local variable escaping function");
1345 let t = ty_of_mlty info ev.e_mlty in
1346 let scall = Option.get (query_syntax info.syntax rs.rs_name) in
1347 let p = Mid.find rs.rs_name info.prec in
1348 let ecall = C.(Esyntax(scall, Tnosyntax, [||], [], p)) in
1349 let d = C.Ddecl (t, [pv_name v, ecall]) in
1350 let d', s = expr info env e in
1351 d::d', s
1352 | Elet (Lvar (a, { e_node = Eapp (rs, [n; v], _) }), e)
1353 when Sattr.mem array_mk rs.rs_name.id_attrs ->
1354 (* let a = Array.make n v in e*)
1355 Debug.dprintf debug_c_extraction "call to an array constructor@.";
1356 if var_escapes_from_expr env a e
1357 then raise (Unsupported "array escaping function");
1358 let env_f = { env with computes_return_value = false } in
1359 let n = expr info env_f n in
1360 let n = get_const_expr n in
1361 let avar = pv_name a in
1362 let sizes = Mid.add avar n env.array_sizes in
1363 let v_ty = ty_of_mlty info v.e_mlty in
1364 let loop_i = id_register (id_fresh "i") in
1365 let d = C.([Ddecl (Tarray (v_ty, n), [avar, Enothing]);
1366 Ddecl (Tsyntax ("int", []), [loop_i, Enothing])]) in
1367 let i = C.Evar loop_i in
1368 let dv, sv = expr info env_f v in
1369 let eai = C.Eindex(Evar avar, i) in
1370 let assign_e = assignify eai sv in
1371 let init_loop = C.(Sfor(Ebinop(Bassign, i,
1372 Econst (Cint ("0", lit_zero))),
1373 Ebinop(Blt, i, n),
1374 Eunop(Upreincr, i),
1375 assign_e)) in
1376 let env = { env with array_sizes = sizes } in
1377 let d', s' = expr info env e in
1378 d@dv@d', Sseq (init_loop, s')
1379 | Elet (ld,e) ->
1380 begin match ld with
1381 | Lvar (pv,le) -> (* not a block *)
1382 let ity = pv.pv_ity in
1383 let cty = ty_of_mlty info le.e_mlty in
1384 do_let pv.pv_vs.vs_name ity cty le e
1385 | Lsym _ -> raise (Unsupported "LDsym")
1386 | Lrec _ -> raise (Unsupported "LDrec") (* TODO for rec at least*)
1387 | Lany _ -> raise (Unsupported "Lany")
1390 let translate_decl (info:info) (d:decl) ~flat ~header : C.definition list =
1391 current_decl_name := "";
1392 let translate_fun rs mlty vl e =
1393 current_decl_name := rs.rs_name.id_string;
1394 Debug.dprintf debug_c_extraction "print %s@." rs.rs_name.id_string;
1395 if rs_ghost rs
1396 then begin Debug.dprintf debug_c_extraction "is ghost@."; [] end
1397 else
1398 let is_dummy id = Sattr.mem Dexpr.dummy_var_attr id.id_attrs in
1399 let boxed = Hreg.create 16 in
1400 let keep_var (id, mlty, gh) =
1401 not gh &&
1402 match mlty with
1403 | Ttuple [] ->
1404 if is_dummy id
1405 then false
1406 else raise (Unsupported "non-dummy unit parameter")
1407 | _ -> true in
1408 let keep_pv pv =
1409 not pv.pv_ghost &&
1410 not (ity_equal pv.pv_ity Ity.ity_unit
1411 && is_dummy pv.pv_vs.vs_name) in
1412 let ngvl = List.filter keep_var vl in
1413 let ngargs = List.filter keep_pv rs.rs_cty.cty_args in
1414 let params =
1415 List.map2 (fun (id, mlty, _gh) pv ->
1416 let cty = ty_of_mlty info mlty in
1417 match has_unboxed_struct cty, pv.pv_ity.ity_node with
1418 | true, Ityreg r ->
1419 Debug.dprintf debug_c_extraction "boxing parameter %s@."
1420 id.id_string;
1421 Hreg.add boxed r ();
1422 C.Tptr cty, id
1423 | _ -> (cty, id)) ngvl ngargs in
1424 let rity = rs.rs_cty.cty_result in
1425 let ret_regs = ity_exp_fold Sreg.add_left Sreg.empty rity in
1426 let is_simple_tuple ity =
1427 let arity_zero = function
1428 | Ityapp(_,a,r) -> a = [] && r = []
1429 | Ityreg { reg_args = a; reg_regs = r } ->
1430 a = [] && r = []
1431 | Ityvar _ -> true
1433 (match ity.ity_node with
1434 | Ityapp ({its_ts = s},_,_)
1435 | Ityreg { reg_its = {its_ts = s}; }
1436 -> is_ts_tuple s
1437 | _ -> false)
1438 && (ity_fold
1439 (fun acc ity ->
1440 acc && arity_zero ity.ity_node) true ity)
1442 (* FIXME is it necessary to have arity 0 in regions ?*)
1443 let rtype, sdecls =
1444 try ty_of_mlty info mlty, []
1445 with Unsupported _ when is_simple_tuple rity ->
1446 let st = struct_of_rs info mlty rs in
1447 C.Tstruct st, [C.Dstruct st] in
1448 (* struct definition goes only in the header *)
1449 let rm_struct_def = function
1450 | C.Dstruct (s,_) ->
1451 Debug.dprintf debug_c_extraction "removing def of %s@." s;
1452 C.Dstruct_decl s
1453 | d -> d in
1454 let sdecls =
1455 if not (header || flat)
1456 then List.map rm_struct_def sdecls
1457 else sdecls in
1458 if has_array rtype then raise (Unsupported "array return");
1459 if header
1460 then
1461 if Hid.mem globals rs.rs_name
1462 then sdecls@[C.Dextern (rtype, rs.rs_name)]
1463 else sdecls@[C.Dproto (rs.rs_name, (rtype, params))]
1464 else
1465 let env = { computes_return_value = true;
1466 in_unguarded_loop = false;
1467 current_function = rs;
1468 ret_regs = ret_regs;
1469 returns = Sid.empty;
1470 breaks = Sid.empty;
1471 array_sizes = Mid.empty;
1472 boxed = boxed;
1473 } in
1474 let d,s = expr info env e in
1475 let d,s = C.flatten_defs d s in
1476 let d = C.group_defs_by_type d in
1477 let s = C.elim_nop s in
1478 let s = C.elim_empty_blocks s in
1479 match s with
1480 | Sreturn r when rs.rs_cty.cty_args = [] ->
1481 Debug.dprintf debug_c_extraction
1482 "declaring global %s@." rs.rs_name.id_string;
1483 Hid.add globals rs.rs_name ();
1484 sdecls@[C.Ddecl (rtype, [rs.rs_name, r])]
1485 | _ -> sdecls@[C.Dfun (rs.rs_name, (rtype,params), (d,s))] in
1487 begin match d with
1488 | Dlet (Lsym(rs, _, mlty, vl, e)) ->
1489 if Sattr.mem Compile.InlineFunctionCalls.inline_attr
1490 rs.rs_name.id_attrs (* call is inlined, do not extract *)
1491 then []
1492 else translate_fun rs mlty vl e
1493 | Dtype [{its_name=id; its_def=idef}] ->
1494 current_decl_name := id.id_string;
1495 Debug.dprintf debug_c_extraction "PDtype %s@." id.id_string;
1496 begin match query_syntax info.syntax id with
1497 | Some _ -> []
1498 | None -> begin
1499 match idef with
1500 | Some (Dalias mlty) ->
1501 let ty = ty_of_mlty info mlty in
1502 Hid.replace aliases id ty;
1504 (*TODO print actual typedef? *)
1505 (*[C.Dtypedef (ty_of_mlty info ty, id)]*)
1506 | Some (Drecord pjl) ->
1507 let pjl =
1508 List.filter
1509 (fun (_,_,ty) ->
1510 match ty with
1511 | Ttuple [] -> false
1512 | _ -> true)
1513 pjl in
1514 let fields =
1515 List.map
1516 (fun (_, id, ty) ->
1517 let cty = ty_of_mlty info ty in
1518 match cty with
1519 | Tstruct _ -> raise (Unsupported "nested structs")
1520 | _ -> (id.id_string, cty))
1521 pjl in
1522 (*if List.exists
1523 (fun (_,t) -> match t with Tarray _ -> true | _ -> false)
1524 fields
1525 then raise (Unsupported "array in struct");*)
1526 let sd = id.id_string, fields in
1527 Hid.replace structs id sd;
1528 if header || flat
1529 then [C.Dstruct sd]
1530 else [C.Dstruct_decl id.id_string]
1531 | Some Ddata _ -> raise (Unsupported "Ddata")
1532 | Some (Drange _) | Some (Dfloat _) ->
1533 raise (Unsupported "Drange/Dfloat")
1534 | None -> raise (Unsupported
1535 ("type declaration without syntax or alias: "
1536 ^id.id_string))
1539 | Dlet (Lrec rl) ->
1540 let translate_rdef rd =
1541 translate_fun rd.rec_sym rd.rec_res rd.rec_args rd.rec_exp in
1542 let defs = List.flatten (List.map translate_rdef rl) in
1543 if header then defs
1544 else
1545 let proto_of_fun = function
1546 | C.Dfun (id, pr, _) -> [C.Dproto (id, pr)]
1547 | _ -> [] in
1548 let protos = List.flatten (List.map proto_of_fun defs) in
1549 protos@defs
1550 | _ -> [] (*TODO exn ? *) end
1551 with
1552 Unsupported s ->
1553 if Debug.test_noflag debug_c_no_error_msgs
1554 then
1555 Format.eprintf
1556 "Could not translate declaration of %s. Unsupported: %s@."
1557 !current_decl_name s;
1560 let translate_decl (info:info) (d:Mltree.decl) ~header ~flat
1561 : C.definition list =
1562 let decide_print id =
1563 (not (header && (Sattr.mem Print.c_static_inline id.id_attrs)))
1564 && query_syntax info.syntax id = None in
1565 let names = Mltree.get_decl_name d in
1566 match List.filter decide_print names with
1567 | [] -> []
1568 | _ -> translate_decl info d ~header ~flat
1572 module Transform = struct
1574 open C
1576 (* Common simplifications *)
1577 let rec expr e =
1578 let e = e_map expr e in
1579 match e with
1580 | Equestion (c, Econst (Cint (_, ic1)), Econst (Cint (_, ic0)))
1581 when is_one ic1 && is_zero ic0 -> c (* c ? 1 : 0 *)
1582 | Equestion (c, Econst (Cint (_, ic0)), Econst (Cint (_, ic1)))
1583 when is_one ic1 && is_zero ic0 -> Eunop (Unot, c) (* c ? 0 : 1 *)
1584 | Eunop (Unot, Ebinop(Beq, e1, e2)) -> Ebinop (Bne, e1, e2) (* ! (e1 == e2) *)
1585 | Edot (Eunop (Ustar, e1), s) -> Earrow (e1, s)
1586 | _ -> e
1588 and stmt s =
1589 let s = s_map def stmt expr s in
1590 match s with
1591 | Sif (Eunop (Unot, Ebinop (Beq, c, Econst (Cint(_, ic0)))), t, e)
1592 | Sif (Ebinop (Bne, c, Econst (Cint (_, ic0))), t, e)
1593 when is_zero ic0 -> Sif (c, t, e) (* if c != 0 *)
1594 | _ -> s
1596 and def (d:definition) = match d with
1597 | C.Dfun (id,p,(dl, s)) -> Dfun (id, p, (List.map def dl, stmt s))
1598 | C.Ddecl (ty, dl) -> C.Ddecl (ty, List.map (fun (id, e) -> id, expr e) dl)
1599 | C.Dextern _
1600 | C.Dproto (_,_) | C.Dstruct _
1601 | C.Dstruct_decl _ | C.Dtypedef (_,_)
1602 | C.Dinclude (_,_) -> d
1604 let defs dl = List.map def dl
1608 let name_gen suffix ?fname m =
1609 let n = m.Pmodule.mod_theory.Theory.th_name.Ident.id_string in
1610 let n = Print.sanitizer n in
1611 let r = match fname with
1612 | None -> n ^ suffix
1613 | Some f -> f ^ "__" ^ n ^ suffix in
1614 Strings.lowercase r
1616 let header_border_printer header _args ?old:_ ?fname fmt m =
1617 let n = Strings.uppercase (name_gen "_H_INCLUDED" ?fname m) in
1618 if header then
1619 Format.fprintf fmt "#ifndef %s@\n@." n
1620 else
1621 Format.fprintf fmt "@\n#define %s@\n#endif // %s@." n n
1623 let print_header_decl args fmt d =
1624 let cds = MLToC.translate_decl args d ~header:true ~flat:false in
1625 List.iter (Format.fprintf fmt "%a@." Print.print_global_def) cds
1627 let mk_info (args:Pdriver.printer_args) m = {
1628 syntax = args.Pdriver.syntax;
1629 literal = args.Pdriver.literal;
1630 prec = args.Pdriver.prec;
1631 kn = m.Pmodule.mod_known }
1633 let print_header_decl =
1634 let memo = Hashtbl.create 16 in
1635 fun args ?old ?fname m fmt d ->
1636 ignore old;
1637 ignore fname;
1638 if not (Hashtbl.mem memo d)
1639 then begin
1640 Hashtbl.add memo d ();
1641 let info = mk_info args m in
1642 print_header_decl info fmt d end
1644 let print_prelude ~header args ?old ?fname ~flat ~deps
1645 ~global_prelude ~prelude fmt pm =
1646 ignore old;
1647 ignore flat;
1648 ignore fname;
1649 Print.init_printers args.Pdriver.blacklist;
1650 let add_include m =
1651 let id = m.Pmodule.mod_theory.Theory.th_name in
1652 Format.fprintf fmt "%a@." Print.print_global_def C.(Dinclude (id,Proj)) in
1653 (* system includes *)
1654 Printer.print_prelude fmt global_prelude;
1655 (* dependencies *)
1656 if header
1657 then
1658 List.iter add_include (List.rev deps)
1659 else
1660 (* C files include only their own header *)
1661 add_include pm;
1662 (* module and exported prelude *)
1663 Printer.print_prelude fmt prelude
1665 let print_decl args fmt d ~flat =
1666 let cds = MLToC.translate_decl args d ~header:false ~flat in
1667 let cds = Transform.defs cds in
1668 let print_def d =
1669 Format.fprintf fmt "%a@." Print.print_global_def d in
1670 List.iter print_def cds
1672 let print_decl ~flat =
1673 let memo = Hashtbl.create 16 in
1674 fun args ?old ?fname m fmt d ->
1675 ignore old;
1676 ignore fname;
1677 ignore m;
1678 Print.init_printers args.Pdriver.blacklist;
1679 if not (Hashtbl.mem memo d)
1680 then begin
1681 Hashtbl.add memo d ();
1682 let info = mk_info args m in
1683 print_decl info fmt d ~flat end
1685 let c_printer = Pdriver.{
1686 desc = "printer for C code";
1687 implem_printer = {
1688 filename_generator = name_gen ".c";
1689 decl_printer = print_decl ~flat:false;
1690 prelude_printer = print_prelude ~flat:false ~header:false;
1691 header_printer = dummy_border_printer;
1692 footer_printer = dummy_border_printer;
1694 interf_printer = Some {
1695 filename_generator = name_gen ".h";
1696 decl_printer = print_header_decl;
1697 prelude_printer = print_prelude ~flat:false ~header:true;
1698 header_printer = header_border_printer true;
1699 footer_printer = header_border_printer false;
1701 flat_printer = {
1702 filename_generator = name_gen ".c";
1703 decl_printer = print_decl ~flat:true;
1704 prelude_printer = print_prelude ~flat:true ~header:false;
1705 header_printer = dummy_border_printer;
1706 footer_printer = dummy_border_printer;
1710 let () =
1711 Pdriver.register_printer "c" c_printer
1714 Local Variables:
1715 compile-command: "unset LANG; make -C ../.."
1716 End: