cduce.ml 13.7 KB
Newer Older
1
open Cduce_loc
2
open Ident
3

4 5
let () = Stats.gettimeofday := Unix.gettimeofday

6
exception Escape of exn
7 8 9
exception InvalidInputFilename of string
exception InvalidObjectFilename of string

10 11
let extra_specs = ref []

12
(* if set to false toplevel exception aren't cought.
13
 * Useful for debugging with OCAMLRUNPARAM="b" *)
14 15
let catch_exceptions = true

16
(* retuns a filename without the suffix suff if any *)
17 18 19 20 21 22 23
let prefix filename suff =
  if Filename.check_suffix filename suff then
    try
      Filename.chop_extension filename
    with Invalid_argument filename -> failwith "Not a point in the suffix?"
  else filename

24
let toplevel = ref false
25
let verbose = ref false
26
let silent = ref false
27

28 29
let typing_env = ref Builtin.env
let compile_env = ref Compile.empty_toplevel
30

31
let get_global_value cenv v =
32
  Eval.eval_var (Compile.find v !compile_env)
33 34 35

let get_global_type v =
  Typer.find_value v !typing_env
36

37 38 39 40 41
let rec is_abstraction = function
  | Ast.Abstraction _ -> true
  | Ast.LocatedExpr (_,e) -> is_abstraction e
  | _ -> false

42
let print_norm ppf d =
43
  Types.Print.pp_type ppf ((*Types.normalize*) d)
44

45
let print_sample ppf s =
46
  Sample.print ppf s
47

48
let print_protect ppf s =
49
  Format.fprintf ppf "%s" s
50

51
let print_value ppf v =
52
  Value.print ppf v
53

54 55
let dump_value ppf x t v =
  Format.fprintf ppf "@[val %a : @[%a = %a@]@]@."
56
    Ident.print x print_norm t print_value v
57

58 59 60
let dump_env ppf tenv cenv =
  Format.fprintf ppf "Types:%a@." Typer.dump_types tenv;
  Format.fprintf ppf "Namespace prefixes:@\n%a" Typer.dump_ns tenv;
61
  Format.fprintf ppf "Namespace prefixes used for pretty-printing:@.%t"
62
    Ns.InternalPrinter.dump;
63
  Format.fprintf ppf "Values:@.";
64
  Typer.iter_values tenv
65 66 67
    (fun x t -> dump_value ppf x t (get_global_value cenv x));
  Format.fprintf ppf "TEnv:%a@." Typer.pp_env tenv;
  Format.fprintf ppf "CEnv:%a@." Compile.pp_env cenv
68

69 70 71
let directive_help ppf =
  Format.fprintf ppf
"Toplevel directives:
72 73 74 75
  #quit;;                 quit the interpreter
  #env;;                  dump current environment
  #reinit_ns;;            reinitialize namespace processing
  #help;;                 shows this help message
76
  #print_type <type>;;    dump internal representation of <type>
77
  #debug ;;
78 79
  #silent;;               turn off outputs from the toplevel
  #verbose;;              turn on outputs from the toplevel
80
  #builtins;;             shows embedded OCaml values
81 82
"

83 84 85
let directive_help_debug ppf =
  Format.fprintf ppf
"Debug sub-directives:
86 87
  #debug subtype <type> <type> ;;     check if t1 < t2 for all substitutions
  #debug bdd  <type>;;                dump the internal type representation
88 89 90 91 92 93 94 95
  #debug typed <expr> ;;              dump typed internal representation
  #debug lambda <expr> ;;             dump lambda internal representation
  #debug accept <???> ;;
  #debug sample <???> ;;
  #debug filter <???> ;;
  #debug compile <???> ;;
"

96
let rec print_exn ppf = function
97
  | Location (loc, w, exn) ->
98
      Cduce_loc.print_loc ppf (loc,w);
99
      Cduce_loc.html_hilight (loc,w);
100
      print_exn ppf exn
101
  | Value.CDuceExn v ->
102
      Format.fprintf ppf "Uncaught CDuce exception: @[%a@]@."
103
        print_value v
104
  | Typer.WrongLabel (t,l) ->
105
      Format.fprintf ppf "Wrong record selection; field %a "
106
        Label.print_attr l;
107
      Format.fprintf ppf "not present in an expression of type:@.%a@."
108
        print_norm t
109
  | Typer.ShouldHave (t,msg) ->
110
      Format.fprintf ppf "This expression should have type:@.%a@.%a@."
111
        print_norm t
112
        print_protect msg
113
  | Typer.ShouldHave2 (t1,msg,t2) ->
114
      Format.fprintf ppf "This expression should have type:@.%a@.%a %a@."
115
        print_norm t1
116
        print_protect msg
117
        print_norm t2
118
  | Typer.Error s ->
119
      Format.fprintf ppf "%a@." print_protect s
120
  | Typer.Constraint (s,t) ->
121
      Format.fprintf ppf "This expression should have type:@.%a@."
122
        print_norm t;
123
      Format.fprintf ppf "but its inferred type is:@.%a@."
124
        print_norm s;
125
      Format.fprintf ppf "which is not a subtype, as shown by the sample:@.%a@."
126
	print_sample (Sample.get (Types.diff s t))
127
  | Typer.NonExhaustive t ->
128 129
      Format.fprintf ppf "This pattern matching is not exhaustive@.";
      Format.fprintf ppf "Residual type:@.%a@."
130
	print_norm t;
131
      Format.fprintf ppf "Sample:@.%a@." print_sample (Sample.get t)
132
  | Typer.UnboundId (x,tn) ->
133
      Format.fprintf ppf "Unbound identifier %a%s@." Ident.print x
134
        (if tn then " (it is a type name)" else "")
135
  | Typer.UnboundExtId (cu,x) ->
136
      Format.fprintf ppf "Unbound external identifier %a:%a@."
137
	U.print (Librarian.name cu)
138
	Ident.print x
139
  | Ulexer.Error (i,j,s) ->
140 141 142
      let loc = Cduce_loc.loc_of_pos (i,j), `Full in
      Cduce_loc.print_loc ppf loc;
      Cduce_loc.html_hilight loc;
143
      Format.fprintf ppf "%s" s
144
  | Parser.Error s | Stream.Error s ->
145
      Format.fprintf ppf "Parsing error: %a@." print_protect s
146
  | Librarian.InconsistentCrc name ->
147
      Format.fprintf ppf "Link error:@.";
148 149 150
      Format.fprintf ppf "Inconsistent checksum (compilation unit: %a)@."
	U.print name
  | Librarian.NoImplementation name ->
151
      Format.fprintf ppf "Link error:@.";
152 153
      Format.fprintf ppf "No implementation found for compilation unit: %a@."
	U.print name
154 155 156 157 158
  | InvalidInputFilename f ->
      Format.fprintf ppf "Compilation error:@.";
      Format.fprintf ppf "Source filename must have extension .cd@.";
  | InvalidObjectFilename f ->
      Format.fprintf ppf "Compilation error:@.";
159
      Format.fprintf ppf "Object filename must have extension .cdo@.";
160 161 162 163
  | Librarian.InvalidObject f ->
      Format.fprintf ppf "Invalid object file %s@." f
  | Librarian.CannotOpen f ->
      Format.fprintf ppf "Cannot open file %s@." f
164
  | Cduce_loc.Generic s ->
165
      Format.fprintf ppf "%a@." print_protect s
166
  | Ns.Label.Not_unique ((ns1,s1),(ns2,s2)) ->
167 168 169 170
      Format.fprintf ppf "Collision on label hash: {%a}:%a, {%a}:%a"
	U.print (Ns.Uri.value ns1)
	U.print s1
	U.print (Ns.Uri.value ns2)
171 172
	U.print s2
  | Ns.Uri.Not_unique (ns1,ns2) ->
173 174
      Format.fprintf ppf "Collision on namespaces hash: %a, %a"
	U.print ns1
175
	U.print ns2
176 177
  | Sequence.Error (Sequence.CopyTag (t,expect)) ->
      Format.fprintf ppf "Tags in %a will be copied, but only %a are allowed.@.Counter-example:%a@."
178 179
	Types.Print.pp_type t
	Types.Print.pp_type expect
180 181 182
	Sample.print (Sample.get (Types.diff t expect))
  | Sequence.Error (Sequence.CopyAttr (t,expect)) ->
      Format.fprintf ppf "Attributes in %a will be copied, but only %a are allowed.@.Counter-example:%a@."
183 184
	Types.Print.pp_type t
	Types.Print.pp_type expect
185 186
	Sample.print (Sample.get (Types.diff t expect))
  | Sequence.Error (Sequence.UnderTag (t,exn)) ->
187
      Format.fprintf ppf "Under tag %a:@." Types.Print.pp_type t;
188
      print_exn ppf exn
189

190
  | exn ->
191
(*      raise exn *)
192
      Format.fprintf ppf "%a@." print_protect (Printexc.to_string exn)
193

194
let eval_quiet tenv cenv e =
195
  let (e,_) = Typer.type_expr tenv e in
196
  Compile.compile_eval_expr cenv e
197

198
let debug ppf tenv cenv = function
199
  | `Subtype (t1,t2) ->
200
      Format.fprintf ppf "[DEBUG:subtype]@.";
201 202
      let t1 = Types.descr (Typer.typ tenv t1)
      and t2 = Types.descr (Typer.typ tenv t2) in
203 204
      let s = Types.subtype t1 t2 in
      Format.fprintf ppf "%a %a %a : %b@." print_norm t1 print_protect "<=" print_norm t2 s
205 206 207 208 209 210 211
  | `Bdd (t) ->
      Format.fprintf ppf "[DEBUG:bdd]@.";
      let t = Types.descr (Typer.typ tenv t) in
      Format.fprintf ppf "@[%a@]@." Types.Print.dump t
  | `Id_bdd (i) ->
      Format.fprintf ppf "[DEBUG:id_bdd]@.";
      Format.fprintf ppf "@[%a@]@." Types.Print.dump_by_id i
212
  | `Sample t ->
213 214
      Format.fprintf ppf "[DEBUG:sample]@.";
      (try
215
	 let t = Types.descr (Typer.typ tenv t) in
216
	 Format.fprintf ppf "%a@." print_sample (Sample.get t);
217
	 Format.fprintf ppf "witness: %a@." Types.Witness.pp (Types.witness t);
218 219
       with Not_found ->
	 Format.fprintf ppf "Empty type : no sample !@.")
220
  | `Filter (t,p) ->
221 222
      let t = Typer.typ tenv t
      and p = Typer.pat tenv p in
223
      Format.fprintf ppf "[DEBUG:filter t=%a p=%a]@."
224
	Types.Print.pp_type (Types.descr t)
225
	Patterns.Print.pp (Patterns.descr p);
226
      let f = Patterns.filter (Types.descr t) p in
227
      IdMap.iteri (fun x t ->
228
		   Format.fprintf ppf " %a:%a@."
229
		     Ident.print x
230 231
		     print_norm (Types.descr t)) f
  | `Accept p ->
232
      Format.fprintf ppf "[DEBUG:accept]@.";
233
      let p = Typer.pat tenv p in
234
      let t = Patterns.accept p in
235
      Format.fprintf ppf " %a@." Types.Print.pp_type (Types.descr t)
236
  | `Compile (t,pl) ->
237
      Format.fprintf ppf "[DEBUG:compile]@.";
238
      let no = ref (-1) in
239
      let t = Types.descr (Typer.typ tenv t)
240 241 242 243 244 245 246 247 248 249
      and pl = List.map (fun p -> incr no; (Typer.pat tenv p, !no)) pl in

      let (state,rhs) = Patterns.Compile.make_branches t pl in
      Array.iteri (fun i r ->
		     Format.fprintf ppf "Return code %i:" i;
		     match r with
		     | Auto_pat.Fail -> Format.fprintf ppf "Fail@."
		     | Auto_pat.Match (_,n) -> Format.fprintf ppf "Pat(%i)@." n
		  ) rhs;
      Format.fprintf ppf "@.Dispatcher:@.%a@." Print_auto.print_state state
250 251 252
  | `Single t ->
      Format.fprintf ppf "[DEBUG:single]@.";
      let t = Typer.typ tenv t in
253
      (try
254
	 let c = Sample.single (Types.descr t) in
255
	 Format.fprintf ppf "Constant:%a@." Types.Print.pp_const c
256
       with
257
	 | Exit -> Format.fprintf ppf "Non constant@."
258
	 | Not_found ->  Format.fprintf ppf "Empty@.")
259 260
  | `Typed e ->
      Format.fprintf ppf "[DEBUG:typed]@.";
261 262 263
      let r, env = Typer.type_expr tenv e in
      Format.fprintf ppf "%a@." Typed.Print.pp r;
      Format.fprintf ppf "%a@." Typer.pp_env tenv
264
  | `Lambda e ->
265
      Format.fprintf ppf "[DEBUG:lambda]@.";
266
      let r, _ = Typer.type_expr tenv e in
267
      let lambdaexpr,lsize = Compile.compile_expr cenv r in
268
      Format.fprintf ppf "%a@." Lambda.Print.pp lambdaexpr
269

270 271 272 273 274 275 276 277 278
let flush_ppf ppf = Format.fprintf ppf "@."

let directive ppf tenv cenv = function
  | `Debug d ->
      debug ppf tenv cenv d
  | `Quit ->
      (if !toplevel then raise End_of_file)
  | `Env ->
      dump_env ppf tenv cenv
279 280
  | `Print_type t ->
      let t = Typer.typ tenv t in
281
      Format.fprintf ppf "%a@." Types.Print.pp_noname (Types.descr t)
282 283
  | `Reinit_ns ->
      Typer.set_ns_table_for_printer tenv
284 285
  | `Help (Some "debug") -> directive_help_debug ppf
  | `Help _ -> directive_help ppf
286 287 288
  | `Dump pexpr ->
      Value.dump_xml ppf (eval_quiet tenv cenv pexpr);
      flush_ppf ppf
289 290 291 292
  | `Silent ->
      silent := true
  | `Verbose ->
      silent := false
293 294 295 296 297
  | `Builtins ->
      let b = Librarian.get_builtins () in
      Format.fprintf ppf "Embedded OCaml value: ";
      List.iter (fun s -> Format.fprintf ppf "%s " s) b;
      Format.fprintf ppf "@."
298 299 300

let print_id_opt ppf = function
  | None -> Format.fprintf ppf "-"
301
  | Some id -> Format.fprintf ppf "val %a" Ident.print id
302 303 304 305 306 307

let print_value_opt ppf = function
  | None -> ()
  | Some v -> Format.fprintf ppf " = %a" print_value v

let show ppf id t v =
308 309
  if !silent then ()
  else Format.fprintf ppf "@[%a : @[%a%a@]@]@."
310 311
    print_id_opt id
    print_norm t
312 313
    print_value_opt v

314
let ev_top ~run ~show ?directive phs =
315
  let (tenv,cenv,_) =
316
    Compile.comp_unit ~run ~show ?directive
317 318 319
      !typing_env !compile_env phs in
  typing_env := tenv;
  compile_env := cenv
320

321 322 323

let phrases ppf phs =
  ev_top ~run:true ~show:(show ppf)  ~directive:(directive ppf) phs
324

325 326 327
let catch_exn ppf_err exn =
  if not catch_exceptions then raise exn;
  match exn with
328 329
  | (End_of_file | Failure _ | Not_found | Invalid_argument _ | Sys.Break)
      as e ->
330
      raise e
331
  | exn ->
332 333 334 335
      print_exn ppf_err exn;
      Format.fprintf ppf_err "@."

let parse rule input =
336 337
  try Parser.localize_exn (fun () -> rule input)
  with e -> Parser.sync (); raise e
338

339
let run rule ppf ppf_err input =
340
  try phrases ppf (parse rule input); true
341
  with Escape exn -> raise exn | exn -> catch_exn ppf_err exn; false
342

343
let topinput = run Parser.top_phrases
344
let script = run Parser.prog
345

346

347
let compile src out_dir =
348
  try
349 350 351
    if not (Filename.check_suffix src ".cd")
    then raise (InvalidInputFilename src);
    let cu = Filename.chop_suffix (Filename.basename src) ".cd" in
352
    let out_dir =
353 354 355 356
      match out_dir with
	| None -> Filename.dirname src
	| Some x -> x in
    let out = Filename.concat out_dir (cu ^ ".cdo") in
357 358
    let name = U.mk_latin1 cu in
    Librarian.compile_save !verbose name src out;
359
    exit 0
360
  with exn -> catch_exn Format.err_formatter exn; exit 1
361

362
let compile_run src =
363
  try
364
    let name =
365 366 367 368 369
      if src = "" then "<stdin>"
      else
	if not (Filename.check_suffix src ".cd")
	then raise (InvalidInputFilename src)
	else Filename.chop_suffix (Filename.basename src) ".cd" in
370 371
    let name = U.mk_latin1 name in
    Librarian.compile_run !verbose name src;
372
  with exn -> catch_exn Format.err_formatter exn; exit 1
373

374
let run obj =
375 376
 Cduce_loc.obj_path := (Filename.dirname obj)::!Cduce_loc.obj_path ;
 let obj = Filename.basename obj in
377
  try
378
    if not (Filename.check_suffix obj ".cdo") then raise (InvalidObjectFilename obj);
379 380 381
    let name = Filename.chop_suffix (Filename.basename obj) ".cdo" in
    let name = U.mk_latin1 name in
    Librarian.load_run name
382
  with exn -> catch_exn Format.err_formatter exn; exit 1
383

384
let dump_env ppf = dump_env ppf !typing_env !compile_env
385 386 387 388 389 390 391

let eval s =
  let st = Stream.of_string s in
  let phs = parse Parser.prog st in
  let vals = ref [] in
  let show id t v =
    match id,v with
392
      | Some id, Some v ->
393
	  vals := (Some (Atoms.V.mk id),v) :: !vals
394 395 396 397
      | None, Some v ->
	  vals := (None,v) :: !vals
      | _ -> assert false
  in
398
  ev_top ~run:true ~show phs;
399
  List.rev !vals
400

401 402
let eval s =
  try eval s
403
  with exn ->
404 405 406 407
    let b = Buffer.create 1024 in
    let ppf = Format.formatter_of_buffer b in
    print_exn ppf exn;
    Format.fprintf ppf "@.";
408 409
    Value.failwith' (Buffer.contents b)

410 411

let () =
412
  Operators.register_fun "eval_expr" Builtin_defs.string_latin1 Types.any
413 414 415 416 417
  (fun v ->
     match eval (Value.cduce2ocaml_string v) with
       | [ (None,v) ] -> v
       | _ -> Value.failwith' "eval: the string must evaluate to a single value"
  )