-
Notifications
You must be signed in to change notification settings - Fork 19
/
spartan.ml
156 lines (127 loc) · 4.31 KB
/
spartan.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
(** The main executable. *)
open Util
(** The usage message. *)
let usage = "Usage: spartan [option] ... [file] ..."
(** A list of files to be loaded and run, together with information on whether they should
be loaded in interactive mode. *)
let files = ref []
(** Add a file to the list of files to be loaded, and record whether it should
be processed in interactive mode. *)
let add_file quiet filename = (files := (filename, quiet) :: !files)
(** Command-line options *)
let options = Arg.align [
("--columns",
Arg.Set_int Config.columns,
" Set the maximum number of columns of pretty printing");
("--wrapper",
Arg.String (fun str -> Config.wrapper := [str]),
"<program> Specify a command-line wrapper to be used (such as rlwrap or ledit)");
("--no-wrapper",
Arg.Unit (fun () -> Config.wrapper := []),
" Do not use a command-line wrapper");
("--no-prelude",
Arg.Unit (fun () -> Config.prelude_file := Config.PreludeNone),
" Do not load the prelude.tt file");
("--prelude",
Arg.String (fun str -> Config.prelude_file := Config.PreludeFile str),
"<file> Specify the prelude file to load initially");
("--ascii",
Arg.Set Config.ascii,
" Use ASCII characters only");
("-V",
Arg.Set_int Config.verbosity,
"<n> Set printing verbosity to <n>");
("-n",
Arg.Clear Config.interactive_shell,
" Do not run the interactive toplevel");
("-l",
Arg.String (fun str -> add_file true str),
"<file> Load <file> into the initial environment");
]
(* Print the error message corresponding to an exception. *)
let print_error ~penv = function
| Parsing.Ulexbuf.Error {Location.data=err; Location.loc} ->
Print.error "Lexical error at %t:@ %t" (Location.print loc) (Parsing.Ulexbuf.print_error err)
| Core.Typecheck.Error {Location.data=err; Location.loc} ->
Print.error "Typechecking error at %t:@ %t"
(Location.print loc)
(Core.Typecheck.print_error ~penv err)
| Sys.Break ->
Print.error "Interrupted." ;
| e ->
raise e
(* Interactive toplevel. *)
let interactive_shell state =
Format.printf "Spartan type theory@." ;
let rec loop state =
let state =
try
Core.Toplevel.exec_interactive state
with
| e ->
print_error ~penv:(Core.Toplevel.penv state) e ; state
in loop state
in
try
loop state
with
End_of_file -> ()
(* The main program. *)
let _main =
Sys.catch_break true ;
(* Parse the arguments. *)
Arg.parse
options
(fun str -> add_file false str ; Config.interactive_shell := false)
usage ;
(* Attempt to wrap yourself with a line-editing wrapper. *)
if !Config.interactive_shell then
begin match !Config.wrapper with
| [] -> ()
| _::_ as lst ->
let n = Array.length Sys.argv + 2 in
let args = Array.make n "" in
Array.blit Sys.argv 0 args 1 (n - 2) ;
args.(n - 1) <- "--no-wrapper" ;
List.iter
(fun wrapper ->
try
args.(0) <- wrapper ;
Unix.execvp wrapper args
with Unix.Unix_error _ -> ())
lst
end ;
(* Files were accumulated in the wrong order, so we reverse them *)
files := List.rev !files ;
(* Should we load the prelude file? *)
begin
match !Config.prelude_file with
| Config.PreludeNone -> ()
| Config.PreludeFile f -> add_file true f
| Config.PreludeDefault ->
(* look for prelude next to the executable and don't whine if it is not there *)
let d = Filename.dirname Sys.argv.(0) in
let f = Filename.concat d "prelude.tt" in
if Sys.file_exists f then add_file true f
end ;
(* Set the maximum depth of pretty-printing, after which it prints ellipsis. *)
Format.set_max_boxes !Config.max_boxes ;
Format.set_margin !Config.columns ;
Format.set_ellipsis_text "..." ;
let rec run_code topstate files =
try
begin
match files with
| [] ->
if !Config.interactive_shell
then interactive_shell topstate
else ()
| (fn, quiet) :: files ->
let topstate = Core.Toplevel.load_file ~quiet topstate fn in
run_code topstate files
end
with
| e ->
print_error ~penv:(Core.Toplevel.penv topstate) e
in
run_code Core.Toplevel.initial !files