-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrunner.ml
159 lines (137 loc) · 5.53 KB
/
runner.ml
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
open Unix
open Filename
open Str
open Compile
open Printf
open OUnit2
open ExtLib
open Lexing
type ('a, 'b) either =
| Left of 'a
| Right of 'b
let either_printer e =
match e with
| Left(v) -> sprintf "Error: %s\n" v
| Right(v) -> v
let string_of_position p =
sprintf "%s:line %d, col %d" p.pos_fname p.pos_lnum (p.pos_cnum - p.pos_bol);;
let parse name lexbuf =
try
lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = name };
Parser.program Lexer.token lexbuf
with
| Failure "lexing: empty token" ->
failwith (sprintf "lexical error at %s"
(string_of_position lexbuf.lex_curr_p))
let parse_string name s =
let lexbuf = Lexing.from_string s in
parse name lexbuf
let parse_file name input_file =
let lexbuf = Lexing.from_channel input_file in
parse name lexbuf
let compile_file_to_string name input_file =
let input_program = parse_file name input_file in
(compile_to_string input_program);;
let compile_string_to_string name s =
let input_program = parse_string name s in
(compile_to_string input_program);;
let make_tmpfiles name =
let (null_stdin, _) = pipe() in
let stdout_name = (temp_file ("stdout_" ^ name) ".out") in
let stdin_name = (temp_file ("stderr_" ^ name) ".err") in
(openfile stdout_name [O_RDWR] 0o600, stdout_name,
openfile stdin_name [O_RDWR] 0o600, stdin_name,
null_stdin)
(* Read a file into a string *)
let string_of_file file_name =
let inchan = open_in file_name in
let buf = String.create (in_channel_length inchan) in
really_input inchan buf 0 (in_channel_length inchan);
buf
type result = (string, string) either
let run_no_vg (program_name : string) : result =
let (rstdout, rstdout_name, rstderr, rstderr_name, rstdin) = make_tmpfiles "run" in
let ran_pid = Unix.create_process (program_name ^ ".run") (Array.of_list []) rstdin rstdout rstderr in
let (_, status) = waitpid [] ran_pid in
let result = match status with
| WEXITED 0 -> Right(string_of_file rstdout_name)
| WEXITED n -> Left(sprintf "Error %d: %s" n (string_of_file rstderr_name))
| WSIGNALED n ->
Left(sprintf "Signalled with %d while running %s." n program_name)
| WSTOPPED n ->
Left(sprintf "Stopped with signal %d while running %s." n program_name) in
List.iter close [rstdout; rstderr; rstdin];
List.iter unlink [rstdout_name; rstderr_name];
result
let run_vg (program_name : string) : result =
let (rstdout, rstdout_name, rstderr, rstderr_name, rstdin) = make_tmpfiles "run" in
let ran_pid = Unix.create_process "valgrind" (Array.of_list [""; (program_name ^ ".run")]) rstdin rstdout rstderr in
let (_, status) = waitpid [] ran_pid in
let vg_str = string_of_file rstderr_name in
let vg_ok = String.exists vg_str "0 errors from 0 contexts" in
let result = match (status, vg_ok) with
| WEXITED 0, true -> Right(string_of_file rstdout_name)
| WEXITED 0, false -> Left("Stdout: " ^ (string_of_file rstdout_name) ^ "\n" ^ "Valgrind: \n" ^ vg_str)
| WEXITED n, _ -> Left(sprintf "Error %d: %s" n vg_str)
| WSIGNALED n, _ ->
Left(sprintf "Signalled with %d while running %s." n program_name)
| WSTOPPED n, _ ->
Left(sprintf "Stopped with signal %d while running %s." n program_name) in
List.iter close [rstdout; rstderr; rstdin];
List.iter unlink [rstdout_name; rstderr_name];
result
let run p (out : string) (runner : string -> result) : result =
let maybe_asm_string =
try Right(compile_to_string p)
with Failure s ->
Left("Compile error: " ^ s)
in
match maybe_asm_string with
| Left(s) -> Left(s)
| Right(asm_string) ->
let outfile = open_out (out ^ ".s") in
fprintf outfile "%s" asm_string;
close_out outfile;
let (bstdout, bstdout_name, bstderr, bstderr_name, bstdin) = make_tmpfiles "build" in
let built_pid = Unix.create_process "make" (Array.of_list [""; out ^ ".run"]) bstdin bstdout bstderr in
let (_, status) = waitpid [] built_pid in
let try_running = match status with
| WEXITED 0 ->
Right(string_of_file bstdout_name)
| WEXITED n ->
Left(sprintf "Finished with error while building %s:\n%s" out (string_of_file bstderr_name))
| WSIGNALED n ->
Left(sprintf "Signalled with %d while building %s." n out)
| WSTOPPED n ->
Left(sprintf "Stopped with signal %d while building %s." n out) in
let result = match try_running with
| Left(_) -> try_running
| Right(msg) ->
runner out in
List.iter close [bstdout; bstderr; bstdin];
List.iter unlink [bstdout_name; bstderr_name];
result
let test_run program_str outfile expected test_ctxt =
let full_outfile = "output/" ^ outfile in
let program = parse_string outfile program_str in
let result = run program full_outfile run_no_vg in
assert_equal (Right(expected ^ "\n")) result ~printer:either_printer
let test_run_valgrind program_str outfile expected test_ctxt =
let full_outfile = "output/" ^ outfile in
let program = parse_string outfile program_str in
let result = run program full_outfile run_vg in
assert_equal (Right(expected ^ "\n")) result ~printer:either_printer
let test_err program_str outfile errmsg test_ctxt =
let full_outfile = "output/" ^ outfile in
let program = parse_string outfile program_str in
let result = run program full_outfile run_no_vg in
assert_equal
(Left(errmsg))
result
~printer:either_printer
~cmp: (fun check result ->
match check, result with
| Left(expect_msg), Left(actual_message) ->
String.exists actual_message expect_msg
| _ -> false
)