1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
(**{1 The command-line compiler} *)
(** Output signature of the functor {!Compiler.Make} *)
module type T = sig
val main: unit -> unit
end
(** Signature for the [Parser] input to the functor {!Compiler.Make} *)
module type PARSER = sig
type token
type program
type fragment_obj
exception Error
val program: (Lexing.lexbuf -> token) -> Lexing.lexbuf -> program
val fragment_obj: (Lexing.lexbuf -> token) -> Lexing.lexbuf -> fragment_obj
end
(** Signature for the [Lexer] input to the functor {!Compiler.Make} *)
module type LEXER = sig
type token
type lexical_error = Illegal_character
exception Lexical_error of lexical_error * int * int
val main: Lexing.lexbuf -> token
end
(** Functor building the compiler structure given a language definition, a lexer and a parser implementation *)
module Make
(L: Host.T)
(Lexer: LEXER)
(Parser: PARSER
with type token = Lexer.token
and type program = L.Syntax.program
and type fragment_obj = L.Syntax.fragment_obj)
: T =
struct
module Error = Error.Make(L)(Lexer)(Parser)
let usage = "usage: rfsmc [options...] files"
let source_files = ref ([] : string list)
let generated_files = ref ([] : string list)
let anonymous fname = source_files := !source_files @ [fname]
let print_banner () =
Printf.printf "---------------------------------------------------------------------------\n";
Printf.printf "Reactive Finite State Machine compiler and simulator, version %s/%s-%s\n"
Version.version L.Guest.Info.name L.Guest.Info.version ;
Printf.printf "For information: github.com/jserot/rfsm\n";
Printf.printf "---------------------------------------------------------------------------\n";
flush stdout
let analyse_file ~lexer:lexer ~parser:parse fname =
let ic = open_in_bin fname in
Location.input_name := fname;
Location.input_chan := ic;
let lexbuf = Lexing.from_channel !Location.input_chan in
Location.input_lexbuf := lexbuf;
parse lexer !Location.input_lexbuf
let analyse_string ~lexer:lexer ~parser:parse s =
let lexbuf = Lexing.from_string s in
Location.input_lexbuf := lexbuf;
parse lexer !Location.input_lexbuf
let add_generated_file f =
generated_files := !generated_files @ [f];
Logfile.write f
let compile () =
let open L in
let p0 =
List.fold_left
(fun p f ->
let p' = analyse_file ~lexer:Lexer.main ~parser:Parser.program f in
Syntax.add_program p p')
Syntax.empty_program
!source_files in
let p = Syntax.ppr_program p0 in
let tenv0 = Typing.mk_env () in
if !Options.dump_tenv then Format.printf "tenv=%a@." pp_tenv tenv0;
let tp = type_program tenv0 p in
if !Options.dump_typed then Format.printf "tp=%a@." Typing.pp_typed_program tp;
let s = elab tp p in
if !Options.dump_static then Format.printf "s=%a@." (Static.pp ~verbose_level:2) s;
Logfile.start ();
begin match !Options.target with
| Some Options.Dot ->
Ext.File.check_dir !Options.target_dir;
let fs = Dot.output_static ~dir:!Options.target_dir ~name:!Options.main_prefix s in
List.iter add_generated_file fs
| Some Options.CTask ->
Ext.File.check_dir !Options.target_dir;
let fs = Ctask.output ~dir:!Options.target_dir s in
List.iter add_generated_file fs
| Some Options.SystemC ->
Ext.File.check_dir !Options.target_dir;
let fs = Systemc.output ~dir:!Options.target_dir ~pfx:!Options.main_prefix s in
List.iter add_generated_file fs
| Some Options.Vhdl ->
Ext.File.check_dir !Options.target_dir;
let fs = Vhdl.output ~dir:!Options.target_dir ~pfx:!Options.main_prefix s in
List.iter add_generated_file fs
| Some Options.Sim ->
if s.fsms <> [] then
let vcd_file = !Options.target_dir ^ "/" ^ !Options.main_prefix ^ ".vcd" in
add_generated_file vcd_file;
run ~vcd_file p s
else begin
Printf.eprintf "No testbench to simulate.\n"; flush stderr;
exit 1
end
| None ->
()
end;
Logfile.stop ()
let check_fragment (type_check:bool) (f: Fragment.t) =
let open L in
try
let mk_iov (id,t) = Ident.mk id, Syntax.mk_basic_type_expr t in
let pf = {
Syntax.pf_inps = List.map mk_iov f.Fragment.inps;
Syntax.pf_outps = List.map mk_iov f.Fragment.outps;
Syntax.pf_vars = List.map mk_iov f.Fragment.vars;
Syntax.pf_obj = analyse_string ~lexer:Lexer.main ~parser:Parser.fragment_obj f.Fragment.obj
} in
let rds,wrs = L.Syntax.check_fragment pf in
if type_check then begin
let ppf = L.Syntax.ppr_fragment pf in
type_fragment ppf
end;
Response.CheckingOk (List.map Ident.to_string rds, List.map Ident.to_string wrs)
with exn ->
if !Options.verbose then (Printf.printf "rfsm server: check_fragment raised %s\n" (Printexc.to_string exn); flush stdout);
begin match exn with
| L.Syntax.Invalid_symbol (id,_,reason) ->
Response.CheckingFailed (Printf.sprintf "symbol %s: %s" (Ident.to_string id) reason)
| Typing.Type_mismatch(_,ty,ty') ->
Response.CheckingFailed (Printf.sprintf "expected type here was %s, not %s\n"
ty
(Ext.Format.to_string L.Typing.HostSyntax.pp_typ ty'))
| L.Guest.Typing.Type_conflict (loc,ty,ty') ->
Response.CheckingFailed (Printf.sprintf "cannot unify types %s and %s"
(Ext.Format.to_string L.Guest.Types.pp_typ ty)
(Ext.Format.to_string L.Guest.Types.pp_typ ty'))
| Parser.Error ->
Response.CheckingFailed "syntax error"
| _ ->
Response.CheckingFailed (Printf.sprintf "syntax error: %s" (Printexc.to_string exn))
end
let handle_request req =
match req with
| Request.GetVersion -> Response.Version Version.version
| Request.CheckFragment pf -> check_fragment true pf
| Request.ScanFragment pf -> check_fragment false pf
| Request.Close -> Response.None
| Request.Compile args ->
Arg.current := 0;
source_files := [];
generated_files := [];
begin try
Arg.parse_argv (Array.of_list (Sys.argv.(0) :: args)) (Options.spec @ L.Guest.Options.specs) anonymous usage;
compile ();
Response.CompilationOk !generated_files
with Arg.Bad m->
Response.CompilationFailed ("illegal compiler argument: " ^ m)
end
exception EndOfService
let service ic oc =
while true do
let response =
try
if !Options.verbose then (Printf.printf "rfsm server: waiting for request\n" ; flush stdout);
let line = input_line ic in
if !Options.verbose then (Printf.printf "rfsm server: got request: %s\n" line ; flush stdout);
let request = Request.of_string line in
if !Options.verbose then (Format.printf "rfsm server: decoded request: %a\n" Request.pp request; flush stdout);
begin match request with
| Request.Close ->
raise EndOfService
| _ ->
handle_request request
end
with
| EndOfService ->
if !Options.verbose then (Format.printf "rfsm server: terminating\n"; flush stdout);
begin try Unix.unlink !Options.socket_path with _ -> () end;
exit 0
| End_of_file ->
Response.None
| Yojson.Json_error _ ->
Response.Error "Ill-formed request"
| exn ->
let m = Printexc.to_string exn in
if !Options.verbose then (Printf.printf "rfsm server: caught exn %s\n" m; flush stdout);
Response.Error m in
let line' = Response.to_string response in
if !Options.verbose then (Printf.printf "rfsm server: encoded response: %s\n" line'; flush stdout);
output_string oc (line' ^ "\n");
flush oc
done
let main () =
try
Sys.catch_break true;
Printexc.record_backtrace !Options.dump_backtrace;
Arg.parse (Options.spec @ L.Guest.Options.specs) anonymous usage;
if !Options.server_mode then
Server.start ~socket:!Options.socket_path ~fn:service
else
begin
print_banner ();
compile ()
end
with
e -> Error.handle e
end