1
|
(* EMF backend *)
|
2
|
(* This backup is initially motivated by the need to express Spacer computed
|
3
|
invariants as Matlab Simulink executable evidences.
|
4
|
|
5
|
Therefore the input language is more restricted. We do not expect fancy
|
6
|
datastructure, complex function calls, etc.
|
7
|
|
8
|
In case of error, use -node foo -inline to eliminate function calls that are
|
9
|
not seriously handled yet.
|
10
|
|
11
|
|
12
|
In terms of algorithm, the process was initially based on printing normalized
|
13
|
code. We now rely on machine code printing. The old code is preserved for
|
14
|
reference.
|
15
|
*)
|
16
|
|
17
|
open LustreSpec
|
18
|
open Machine_code
|
19
|
open Format
|
20
|
open Utils
|
21
|
|
22
|
exception Unhandled of string
|
23
|
|
24
|
|
25
|
(* Basic printing functions *)
|
26
|
|
27
|
let pp_var_string fmt v = fprintf fmt "\"%s\"" v
|
28
|
let pp_var_name fmt v = fprintf fmt "\"%a\"" Printers.pp_var_name v
|
29
|
let pp_node_args = fprintf_list ~sep:", " pp_var_name
|
30
|
|
31
|
|
32
|
(* Matlab starting counting from 1.
|
33
|
simple function to extract the element id in the list. Starts from 1. *)
|
34
|
let rec get_idx x l =
|
35
|
match l with
|
36
|
| hd::tl -> if hd = x then 1 else 1+(get_idx x tl)
|
37
|
| [] -> assert false
|
38
|
|
39
|
(**********************************************)
|
40
|
(* Old stuff: printing normalized code as EMF *)
|
41
|
(**********************************************)
|
42
|
|
43
|
(*
|
44
|
let pp_expr vars fmt expr =
|
45
|
let rec pp_expr fmt expr =
|
46
|
match expr.expr_desc with
|
47
|
| Expr_const c -> Printers.pp_const fmt c
|
48
|
| Expr_ident id ->
|
49
|
if List.mem id vars then
|
50
|
Format.fprintf fmt "u%i" (get_idx id vars)
|
51
|
else
|
52
|
assert false (* impossible to find element id in var list *)
|
53
|
| Expr_array a -> fprintf fmt "[%a]" pp_tuple a
|
54
|
| Expr_access (a, d) -> fprintf fmt "%a[%a]" pp_expr a Dimension.pp_dimension d
|
55
|
| Expr_power (a, d) -> fprintf fmt "(%a^%a)" pp_expr a Dimension.pp_dimension d
|
56
|
| Expr_tuple el -> fprintf fmt "(%a)" pp_tuple el
|
57
|
| Expr_ite (c, t, e) -> fprintf fmt "if %a; y=(%a); else y=(%a); end" pp_expr c pp_expr t pp_expr e
|
58
|
| Expr_arrow (e1, e2) ->(
|
59
|
match e1.expr_desc, e2.expr_desc with
|
60
|
| Expr_const c1, Expr_const c2 -> if c1 = Corelang.const_of_bool true && c2 = Corelang.const_of_bool false then fprintf fmt "STEP" else assert false (* only handle true -> false *)
|
61
|
| _ -> assert false (* only handle true -> false *)
|
62
|
)
|
63
|
| Expr_fby (e1, e2) -> assert false (* not covered yet *)
|
64
|
| Expr_pre e -> fprintf fmt "UNITDELAY"
|
65
|
| Expr_when (e, id, l) -> assert false (* clocked based expressions are not handled yet *)
|
66
|
| Expr_merge (id, hl) -> assert false (* clocked based expressions are not handled yet *)
|
67
|
| Expr_appl (id, e, r) -> pp_app fmt id e r
|
68
|
|
69
|
and pp_tuple fmt el =
|
70
|
fprintf_list ~sep:"," pp_expr fmt el
|
71
|
|
72
|
and pp_app fmt id e r =
|
73
|
match r with
|
74
|
| None -> pp_call fmt id e
|
75
|
| Some c -> assert false (* clocked based expressions are not handled yet *)
|
76
|
|
77
|
and pp_call fmt id e =
|
78
|
match id, e.expr_desc with
|
79
|
| "+", Expr_tuple([e1;e2]) -> fprintf fmt "(%a + %a)" pp_expr e1 pp_expr e2
|
80
|
| "uminus", _ -> fprintf fmt "(- %a)" pp_expr e
|
81
|
| "-", Expr_tuple([e1;e2]) -> fprintf fmt "(%a - %a)" pp_expr e1 pp_expr e2
|
82
|
| "*", Expr_tuple([e1;e2]) -> fprintf fmt "(%a * %a)" pp_expr e1 pp_expr e2
|
83
|
| "/", Expr_tuple([e1;e2]) -> fprintf fmt "(%a / %a)" pp_expr e1 pp_expr e2
|
84
|
| "mod", Expr_tuple([e1;e2]) -> fprintf fmt "mod (%a, %a)" pp_expr e1 pp_expr e2
|
85
|
| "&&", Expr_tuple([e1;e2]) -> fprintf fmt "(%a & %a)" pp_expr e1 pp_expr e2
|
86
|
| "||", Expr_tuple([e1;e2]) -> fprintf fmt "(%a | %a)" pp_expr e1 pp_expr e2
|
87
|
| "xor", Expr_tuple([e1;e2]) -> fprintf fmt "xor (%a, %a)" pp_expr e1 pp_expr e2
|
88
|
| "impl", Expr_tuple([e1;e2]) -> fprintf fmt "((~%a) | %a)" pp_expr e1 pp_expr e2
|
89
|
| "<", Expr_tuple([e1;e2]) -> fprintf fmt "(%a < %a)" pp_expr e1 pp_expr e2
|
90
|
| "<=", Expr_tuple([e1;e2]) -> fprintf fmt "(%a <= %a)" pp_expr e1 pp_expr e2
|
91
|
| ">", Expr_tuple([e1;e2]) -> fprintf fmt "(%a > %a)" pp_expr e1 pp_expr e2
|
92
|
| ">=", Expr_tuple([e1;e2]) -> fprintf fmt "(%a >= %a)" pp_expr e1 pp_expr e2
|
93
|
| "!=", Expr_tuple([e1;e2]) -> fprintf fmt "(%a ~= %a)" pp_expr e1 pp_expr e2
|
94
|
| "=", Expr_tuple([e1;e2]) -> fprintf fmt "(%a == %a)" pp_expr e1 pp_expr e2
|
95
|
| "not", _ -> fprintf fmt "(~%a)" pp_expr e
|
96
|
| _, Expr_tuple _ -> fprintf fmt "%s %a" id pp_expr e
|
97
|
| _ -> fprintf fmt "%s (%a)" id pp_expr e
|
98
|
|
99
|
in
|
100
|
pp_expr fmt expr
|
101
|
|
102
|
let pp_stmt fmt stmt =
|
103
|
match stmt with
|
104
|
| Eq eq -> (
|
105
|
match eq.eq_lhs with
|
106
|
[var] -> (
|
107
|
(* first, we extract the expression and associated variables *)
|
108
|
let vars = Utils.ISet.elements (Corelang.get_expr_vars eq.eq_rhs) in
|
109
|
|
110
|
fprintf fmt "\"%s\": @[<v 2>{ \"expr\": \"%a\",@ \"vars\": [%a] @]}"
|
111
|
var
|
112
|
(pp_expr vars) eq.eq_rhs (* todo_pp_expr expr *)
|
113
|
(fprintf_list ~sep:", " pp_var_string) vars
|
114
|
)
|
115
|
| _ -> assert false (* should not happen for input of EMF backend (cocospec generated nodes *)
|
116
|
)
|
117
|
| _ -> assert false (* should not happen with EMF backend *)
|
118
|
|
119
|
let pp_node fmt nd =
|
120
|
fprintf fmt "@[<v 2>\"%s\": {@ \"inputs\": [%a],@ \"outputs\": [%a],@ "
|
121
|
nd.node_id
|
122
|
pp_node_args nd.node_inputs
|
123
|
pp_node_args nd.node_outputs;
|
124
|
fprintf fmt "\"exprs\": {@[<v 1> %a@]@ }"
|
125
|
(fprintf_list ~sep:",@ " pp_stmt ) nd.node_stmts;
|
126
|
fprintf fmt "@]@ }"
|
127
|
|
128
|
let pp_decl fmt decl =
|
129
|
match decl.top_decl_desc with
|
130
|
| Node nd -> fprintf fmt "%a@ " pp_node nd
|
131
|
| ImportedNode _
|
132
|
| Const _
|
133
|
| Open _
|
134
|
| TypeDef _ -> eprintf "should not happen in EMF backend"
|
135
|
*)
|
136
|
|
137
|
|
138
|
(**********************************************)
|
139
|
(* Printing machine code as EMF *)
|
140
|
(**********************************************)
|
141
|
|
142
|
(* Print machine code values as matlab expressions. Variable identifiers are
|
143
|
replaced by uX where X is the index of the variables in the list vars of input
|
144
|
variables. *)
|
145
|
let rec pp_val vars fmt v =
|
146
|
match v.value_desc with
|
147
|
| Cst c -> Printers.pp_const fmt c
|
148
|
| LocalVar v
|
149
|
| StateVar v ->
|
150
|
let id = v.var_id in
|
151
|
if List.mem id vars then
|
152
|
Format.fprintf fmt "u%i" (get_idx id vars)
|
153
|
else
|
154
|
assert false (* impossible to find element id in var list *)
|
155
|
| Fun (n, vl) -> pp_fun vars n fmt vl
|
156
|
| _ -> assert false (* not available in EMF backend *)
|
157
|
and pp_fun vars id fmt vl =
|
158
|
(* eprintf "print %s with %i args@.@?" id (List.length vl);*)
|
159
|
match id, vl with
|
160
|
| "+", [v1;v2] -> fprintf fmt "(%a + %a)" (pp_val vars) v1 (pp_val vars) v2
|
161
|
| "uminus", [v] -> fprintf fmt "(- %a)" (pp_val vars) v
|
162
|
| "-", [v1;v2] -> fprintf fmt "(%a - %a)" (pp_val vars) v1 (pp_val vars) v2
|
163
|
| "*",[v1;v2] -> fprintf fmt "(%a * %a)" (pp_val vars) v1 (pp_val vars) v2
|
164
|
| "/", [v1;v2] -> fprintf fmt "(%a / %a)" (pp_val vars) v1 (pp_val vars) v2
|
165
|
| "mod", [v1;v2] -> fprintf fmt "mod (%a, %a)" (pp_val vars) v1 (pp_val vars) v2
|
166
|
| "&&", [v1;v2] -> fprintf fmt "(%a & %a)" (pp_val vars) v1 (pp_val vars) v2
|
167
|
| "||", [v1; v2] -> fprintf fmt "(%a | %a)" (pp_val vars) v1 (pp_val vars) v2
|
168
|
| "xor", [v1; v2] -> fprintf fmt "xor (%a, %a)" (pp_val vars) v1 (pp_val vars) v2
|
169
|
| "impl", [v1; v2] -> fprintf fmt "((~%a) | %a)" (pp_val vars) v1 (pp_val vars) v2
|
170
|
| "<", [v1; v2] -> fprintf fmt "(%a < %a)" (pp_val vars) v1 (pp_val vars) v2
|
171
|
| "<=", [v1; v2] -> fprintf fmt "(%a <= %a)" (pp_val vars) v1 (pp_val vars) v2
|
172
|
| ">", [v1; v2] -> fprintf fmt "(%a > %a)" (pp_val vars) v1 (pp_val vars) v2
|
173
|
| ">=", [v1; v2] -> fprintf fmt "(%a >= %a)" (pp_val vars) v1 (pp_val vars) v2
|
174
|
| "!=", [v1; v2] -> fprintf fmt "(%a != %a)" (pp_val vars) v1 (pp_val vars) v2
|
175
|
| "=", [v1; v2] -> fprintf fmt "(%a = %a)" (pp_val vars) v1 (pp_val vars) v2
|
176
|
| "not", [v] -> fprintf fmt "(~%a)" (pp_val vars) v
|
177
|
| _ -> fprintf fmt "%s (%a)" id (Utils.fprintf_list ~sep:", " (pp_val vars)) vl
|
178
|
|
179
|
|
180
|
(* detect whether the instruction i represents an ARROW, ie an arrow with true -> false *)
|
181
|
let is_arrow_fun m i =
|
182
|
match Corelang.get_instr_desc i with
|
183
|
| MStep ([var], i, vl) -> (
|
184
|
let name = try (Machine_code.get_node_def i m).node_id with Not_found -> Format.eprintf "Impossible to find node %s@.@?" i; raise Not_found in
|
185
|
match name, vl with
|
186
|
| "_arrow", [v1; v2] -> (
|
187
|
match v1.value_desc, v2.value_desc with
|
188
|
| Cst c1, Cst c2 ->
|
189
|
if c1 = Corelang.const_of_bool true && c2 = Corelang.const_of_bool false then
|
190
|
true
|
191
|
else
|
192
|
assert false (* only handle true -> false *)
|
193
|
| _ -> assert false
|
194
|
)
|
195
|
| _ -> false
|
196
|
)
|
197
|
| _ -> false
|
198
|
|
199
|
(* pp_basic_instr prints regular instruction. These do not contain MStep which
|
200
|
should have been already filtered out. Another restriction which is supposed
|
201
|
to be enforced is that branching statement contain a single instruction (in
|
202
|
practice it has to be an assign) *)
|
203
|
let rec pp_basic_instr m vars fmt i =
|
204
|
match Corelang.get_instr_desc i with
|
205
|
| MLocalAssign (var,v)
|
206
|
| MStateAssign (var,v) -> fprintf fmt "y = %a" (pp_val vars) v
|
207
|
| MBranch (g,[(tag1,[case1]);(tag2,[case2])]) ->
|
208
|
(* Thanks to normalization with join_guards = false, branches shall contain
|
209
|
a single expression *)
|
210
|
let then_case, else_case =
|
211
|
if tag1 = Corelang.tag_true then
|
212
|
case1, case2
|
213
|
else
|
214
|
case2, case1
|
215
|
in
|
216
|
fprintf fmt "if %a; %a; else %a; end"
|
217
|
(pp_val vars) g
|
218
|
(pp_basic_instr m vars) then_case
|
219
|
(pp_basic_instr m vars) else_case
|
220
|
| MBranch _ (* EMF backend only accept true/false ite *)
|
221
|
-> Format.eprintf "unhandled branch in EMF@.@?"; assert false
|
222
|
| MReset _
|
223
|
-> Format.eprintf "unhandled reset in EMF@.@?"; assert false
|
224
|
| MNoReset _
|
225
|
-> Format.eprintf "unhandled noreset in EMF@.@?"; assert false
|
226
|
| MStep _ (* function calls already handled, including STEP *)
|
227
|
-> Format.eprintf "unhandled function call in EMF (should have been filtered out before)@.@?";
|
228
|
assert false
|
229
|
| MComment _
|
230
|
-> Format.eprintf "unhandled comment in EMF@.@?"; assert false
|
231
|
(* not available for EMF output *)
|
232
|
|
233
|
|
234
|
|
235
|
let rec get_instr_var i =
|
236
|
match Corelang.get_instr_desc i with
|
237
|
| MLocalAssign (var,_)
|
238
|
| MStateAssign (var,_)
|
239
|
| MStep ([var], _, _) -> var
|
240
|
| MBranch (_,[(tag1,case1);(tag2,case2)]) ->
|
241
|
get_instrs_var case1 (* assuming case1 and case2 define the same variable *)
|
242
|
| MStep _ (* only single output for function call *)
|
243
|
| MBranch _ (* EMF backend only accept true/false ite *)
|
244
|
| MReset _
|
245
|
| MNoReset _
|
246
|
| MComment _ -> assert false (* not available for EMF output *)
|
247
|
and get_instrs_var il =
|
248
|
match il with
|
249
|
| i::_ -> get_instr_var i (* looking for the first instr *)
|
250
|
| _ -> assert false
|
251
|
|
252
|
|
253
|
let rec get_val_vars v =
|
254
|
match v.value_desc with
|
255
|
| Cst c -> Utils.ISet.empty
|
256
|
| LocalVar v
|
257
|
| StateVar v -> Utils.ISet.singleton v.var_id
|
258
|
| Fun (n, vl) -> List.fold_left (fun res v -> Utils.ISet.union (get_val_vars v) res) Utils.ISet.empty vl
|
259
|
| _ -> assert false (* not available in EMF backend *)
|
260
|
|
261
|
let rec get_instr_vars i =
|
262
|
match Corelang.get_instr_desc i with
|
263
|
| MLocalAssign (_,v)
|
264
|
| MStateAssign (_,v) -> get_val_vars v
|
265
|
| MStep (_, _, vl) -> List.fold_left (fun res v -> Utils.ISet.union res (get_val_vars v)) Utils.ISet.empty vl
|
266
|
| MBranch (c,[(_,[case1]);(_,[case2])]) ->
|
267
|
Utils.ISet.union
|
268
|
(get_val_vars c)
|
269
|
(
|
270
|
Utils.ISet.union
|
271
|
(get_instr_vars case1)
|
272
|
(get_instr_vars case2)
|
273
|
)
|
274
|
| MBranch _ (* EMF backend only accept true/false ite *)
|
275
|
| MReset _
|
276
|
| MNoReset _
|
277
|
| MComment _ -> failwith "Error in compiling some constructs into EMF. Have you considered -node foo -inline options ?" (* not available for EMF output *)
|
278
|
(* and get_instrs_vars il = *)
|
279
|
(* List.fold_left (fun res i -> Utils.ISet.union res (get_instr_vars i)) *)
|
280
|
(* Utils.ISet.empty *)
|
281
|
(* il *)
|
282
|
|
283
|
|
284
|
let pp_original_lustre_expression m fmt i =
|
285
|
match Corelang.get_instr_desc i with
|
286
|
| MLocalAssign _ | MStateAssign _
|
287
|
| MBranch _
|
288
|
-> ( match i.lustre_eq with None -> () | Some e -> Printers.pp_node_eq fmt e)
|
289
|
| MStep _ when is_arrow_fun m i -> () (* we print nothing, this is a STEP *)
|
290
|
| MStep _ -> (match i.lustre_eq with None -> () | Some eq -> Printers.pp_node_eq fmt eq)
|
291
|
| _ -> ()
|
292
|
|
293
|
let pp_emf_instrs m fmt i =
|
294
|
(* Either it is a Step function non arrow, then we have a dedicated treatment,
|
295
|
or it has to be a single variable assigment *)
|
296
|
let arguments_vars = Utils.ISet.elements (get_instr_vars i) in
|
297
|
|
298
|
match Corelang.get_instr_desc i with
|
299
|
(* Regular node call either a statuful node or a functional one *)
|
300
|
MStep (outputs, f, inputs) when not (is_arrow_fun m i) -> (
|
301
|
fprintf fmt "\"__functioncall\": @[<v 2>{ \"node\": \"%s\",@ \"inputs\": [%a],@ \"vars\": [%a]@ \"outputs\": [%a],@ \"original_lustre_expr\": [%a]@]}"
|
302
|
((Machine_code.get_node_def f m).node_id) (* Node name *)
|
303
|
(Utils.fprintf_list ~sep:", " (fun fmt _val -> fprintf fmt "\"%a\"" (pp_val arguments_vars) _val)) inputs (* inputs *)
|
304
|
(fprintf_list ~sep:", " pp_var_string) arguments_vars
|
305
|
(fprintf_list ~sep:", " (fun fmt v -> pp_var_string fmt v.var_id)) outputs (* outputs *)
|
306
|
(pp_original_lustre_expression m) i (* original lustre expr *)
|
307
|
)
|
308
|
| _ ->
|
309
|
(* Other expressions, including "pre" *)
|
310
|
(
|
311
|
(* first, we extract the expression and associated variables *)
|
312
|
let var = get_instr_var i in
|
313
|
fprintf fmt "\"%s\": @[<v 2>{ \"expr\": \"%a\",@ \"vars\": [%a] @ \"original_lustre_expr\": [%a]@]}"
|
314
|
var.var_id
|
315
|
(fun fmt i -> match Corelang.get_instr_desc i with
|
316
|
| MStep _ -> fprintf fmt "STEP"
|
317
|
| _ -> pp_basic_instr m arguments_vars fmt i) i
|
318
|
(fprintf_list ~sep:", " pp_var_string) arguments_vars
|
319
|
(pp_original_lustre_expression m) i
|
320
|
)
|
321
|
|
322
|
|
323
|
let pp_machine fmt m =
|
324
|
try
|
325
|
fprintf fmt "@[<v 2>\"%s\": {@ \"inputs\": [%a],@ \"outputs\": [%a],@ "
|
326
|
m.mname.node_id
|
327
|
pp_node_args m.mstep.step_inputs
|
328
|
pp_node_args m.mstep.step_outputs;
|
329
|
fprintf fmt "\"exprs\": {@[<v 1> %a@]@ }"
|
330
|
(fprintf_list ~sep:",@ " (pp_emf_instrs m)) m.mstep.step_instrs;
|
331
|
fprintf fmt "@]@ }"
|
332
|
with Unhandled msg -> (
|
333
|
eprintf "[Error] @[<v 0>EMF backend@ Issues while translating node %s@ "
|
334
|
m.mname.node_id;
|
335
|
eprintf "%s@ " msg;
|
336
|
eprintf "node skipped - no output generated@ @]@."
|
337
|
)
|
338
|
|
339
|
(****************************************************)
|
340
|
(* Main function: iterates over node and print them *)
|
341
|
(****************************************************)
|
342
|
let pp_meta fmt basename =
|
343
|
fprintf fmt "\"meta\": @[<v 0>{@ ";
|
344
|
Utils.fprintf_list ~sep:",@ "
|
345
|
(fun fmt (k, v) -> fprintf fmt "\"%s\": \"%s\"" k v)
|
346
|
fmt
|
347
|
["compiler-name", (Filename.basename Sys.executable_name);
|
348
|
"compiler-version", Version.number;
|
349
|
"command", (String.concat " " (Array.to_list Sys.argv));
|
350
|
"source_file", basename
|
351
|
]
|
352
|
;
|
353
|
fprintf fmt "@ @]},@ "
|
354
|
|
355
|
let translate fmt basename prog machines =
|
356
|
fprintf fmt "@[<v 0>{@ ";
|
357
|
pp_meta fmt basename;
|
358
|
fprintf fmt "\"nodes\": @[<v 0>{@ ";
|
359
|
(* Previous alternative: mapping normalized lustre to EMF:
|
360
|
fprintf_list ~sep:",@ " pp_decl fmt prog; *)
|
361
|
fprintf_list ~sep:",@ " pp_machine fmt machines;
|
362
|
fprintf fmt "@ @]}";
|
363
|
fprintf fmt "@ @]}"
|
364
|
|
365
|
(* Local Variables: *)
|
366
|
(* compile-command: "make -C ../.." *)
|
367
|
(* End: *)
|