cduce.ml 12.7 KB
Newer Older
1
open Location
2
open Ident
3

4
exception Escape of exn
5 6 7
exception InvalidInputFilename of string
exception InvalidObjectFilename of string

8 9
(* if set to false toplevel exception aren't cought. 
 * Useful for debugging with OCAMLRUNPARAM="b" *)
10 11
let catch_exceptions = true

12 13 14 15 16 17 18 19
(* retuns a filename without the suffix suff if any *) 
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

20
let toplevel = ref false
21
let verbose = ref false
22

23
let typing_env = State.ref "Cduce.typing_env" Builtin.env
24
let compile_env = State.ref "Cduce.compile_env" Compile.empty_toplevel
25

26
let get_global_value cenv v =
27
  Eval.var (Compile.find v !compile_env)
28 29 30

let get_global_type v =
  Typer.find_value v !typing_env
31

32 33 34 35 36
let rec is_abstraction = function
  | Ast.Abstraction _ -> true
  | Ast.LocatedExpr (_,e) -> is_abstraction e
  | _ -> false

37
let print_norm ppf d = 
38
  Location.protect ppf 
39
    (fun ppf -> Types.Print.print ppf ((*Types.normalize*) d))
40

41 42 43 44
let print_sample ppf s =
  Location.protect ppf
    (fun ppf -> Sample.print ppf s)

45 46 47
let print_protect ppf s =
  Location.protect ppf (fun ppf -> Format.fprintf ppf "%s" s)

48 49
let print_value ppf v =
  Location.protect ppf (fun ppf -> Value.print ppf v)
50

51 52 53 54
let dump_value ppf x t v =
  Format.fprintf ppf "@[val %a : @[%a = %a@]@]@."
    U.print (Id.value x) print_norm t print_value v

55 56 57
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;
58
  Format.fprintf ppf "Namespace prefixes used for pretty-printing:@.%t"
59
    Ns.InternalPrinter.dump;
60
  Format.fprintf ppf "Schemas: %s@."
61
    (String.concat " " (List.map U.get_str (Typer.get_schema_names tenv)));
62
  Format.fprintf ppf "Values:@.";
63 64
  Typer.iter_values tenv
    (fun x t -> dump_value ppf x t (get_global_value cenv x))
65

66 67 68
let directive_help ppf =
  Format.fprintf ppf
"Toplevel directives:
69 70 71 72 73 74 75 76
  #quit;;                 quit the interpreter
  #env;;                  dump current environment
  #reinit_ns;;            reinitialize namespace processing
  #help;;                 shows this help message
  #dump_value <expr>;;    dump an XML-ish representation of the resulting
                          value of a given expression
  #print_schema <name>;;
  #print_type <name>;;
77 78
"

79
let rec print_exn ppf = function
80 81
  | Location (loc, w, exn) ->
      Format.fprintf ppf "Error %a:@." Location.print_loc (loc,w);
82
      Format.fprintf ppf "%a" Location.html_hilight (loc,w); 
83
      print_exn ppf exn
84
  | Value.CDuceExn v ->
85
      Format.fprintf ppf "Uncaught CDuce exception: @[%a@]@."
86
        print_value v
87
  | Typer.WrongLabel (t,l) ->
88
      Format.fprintf ppf "Wrong record selection; field %a " 
89
        Label.print (LabelPool.value l);
90
      Format.fprintf ppf "not present in an expression of type:@.%a@."
91
        print_norm t
92
  | Typer.ShouldHave (t,msg) ->
93
      Format.fprintf ppf "This expression should have type:@.%a@.%a@." 
94
        print_norm t
95
        print_protect msg
96
  | Typer.ShouldHave2 (t1,msg,t2) ->
97
      Format.fprintf ppf "This expression should have type:@.%a@.%a %a@." 
98
        print_norm t1
99
        print_protect msg
100
        print_norm t2
101
  | Typer.Error s ->
102
      Format.fprintf ppf "%a@." print_protect s
103
  | Typer.Constraint (s,t) ->
104
      Format.fprintf ppf "This expression should have type:@.%a@." 
105
        print_norm t;
106
      Format.fprintf ppf "but its inferred type is:@.%a@." 
107
        print_norm s;
108
      Format.fprintf ppf "which is not a subtype, as shown by the sample:@.%a@." 
109
	print_sample (Sample.get (Types.diff s t))
110
  | Typer.NonExhaustive t ->
111 112
      Format.fprintf ppf "This pattern matching is not exhaustive@.";
      Format.fprintf ppf "Residual type:@.%a@."
113
	print_norm t;
114
      Format.fprintf ppf "Sample:@.%a@." print_sample (Sample.get t)
115 116 117
  | Typer.UnboundId (x,tn) ->
      Format.fprintf ppf "Unbound identifier %a%s@." U.print (Id.value x)
        (if tn then " (it is a type name)" else "")
118 119 120 121
  | Typer.UnboundExtId (cu,x) ->
      Format.fprintf ppf "Unbound external identifier %a:%a@." 
	U.print (Types.CompUnit.value cu)
	U.print (Id.value x)
122 123 124 125
  | Ulexer.Error (i,j,s) ->
      let loc = Location.loc_of_pos (i,j), `Full in
      Format.fprintf ppf "Error %a:@." Location.print_loc loc;
      Format.fprintf ppf "%a%s" Location.html_hilight loc s
126
  | Parser.Error s | Stream.Error s -> 
127
      Format.fprintf ppf "Parsing error: %a@." print_protect s
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
  | Librarian.InconsistentCrc id ->
      Format.fprintf ppf "Link error:@.";
      let name = Encodings.Utf8.to_string (Types.CompUnit.value id) in
      Format.fprintf ppf "Inconsistent checksum (compilation unit: %s)@."
	name
  | Librarian.NoImplementation id ->
      Format.fprintf ppf "Link error:@.";
      let name = Encodings.Utf8.to_string (Types.CompUnit.value id) in
      Format.fprintf ppf "No implementation found for compilation unit: %s@."
	name
  | Librarian.Loop id ->
      Format.fprintf ppf "Compilation error:@.";
      let name = Encodings.Utf8.to_string (Types.CompUnit.value id) in
      Format.fprintf ppf "Loop between compilation unit (compilation unit: %s)@."
	name
  | InvalidInputFilename f ->
      Format.fprintf ppf "Compilation error:@.";
      Format.fprintf ppf "Source filename must have extension .cd@.";
  | InvalidObjectFilename f ->
      Format.fprintf ppf "Compilation error:@.";
148
      Format.fprintf ppf "Object filename must have extension .cdo and no path@.";
149 150 151 152
  | Librarian.InvalidObject f ->
      Format.fprintf ppf "Invalid object file %s@." f
  | Librarian.CannotOpen f ->
      Format.fprintf ppf "Cannot open file %s@." f
153
  | Location.Generic s ->
154
      Format.fprintf ppf "%a@." print_protect s
155
  | exn ->
156
(*      raise exn *)
157
      Format.fprintf ppf "%a@." print_protect (Printexc.to_string exn)
158

159

160 161
let eval_quiet tenv cenv e = 
  let (e,_) = Typer.type_expr tenv e in
162
  let e = Compile.compile_expr cenv e in 
163 164
  Eval.expr e

165
let debug ppf tenv cenv = function
166
  | `Subtype (t1,t2) ->
167
      Format.fprintf ppf "[DEBUG:subtype]@.";
168 169
      let t1 = Types.descr (Typer.typ tenv t1)
      and t2 = Types.descr (Typer.typ tenv t2) in
170 171
      let s = Types.subtype t1 t2 in
      Format.fprintf ppf "%a %a %a : %b@." print_norm t1 print_protect "<=" print_norm t2 s
172
  | `Sample t ->
173 174
      Format.fprintf ppf "[DEBUG:sample]@.";
      (try
175
	 let t = Types.descr (Typer.typ tenv t) in
176 177 178
	 Format.fprintf ppf "%a@." print_sample (Sample.get t)
       with Not_found ->
	 Format.fprintf ppf "Empty type : no sample !@.")
179
  | `Filter (t,p) -> 
180 181
      let t = Typer.typ tenv t
      and p = Typer.pat tenv p in
182 183 184
      Format.fprintf ppf "[DEBUG:filter t=%a p=%a]@."
	Types.Print.print (Types.descr t)
	Patterns.Print.print (Patterns.descr p);
185 186
      let f = Patterns.filter (Types.descr t) p in
      List.iter (fun (x,t) ->
187
		   Format.fprintf ppf " %a:%a@." U.print (Id.value x)
188 189
		     print_norm (Types.descr t)) f
  | `Accept p ->
190
      Format.fprintf ppf "[DEBUG:accept]@.";
191
      let p = Typer.pat tenv p in
192
      let t = Patterns.accept p in
193
      Format.fprintf ppf " %a@." Types.Print.print (Types.descr t)
194
  | `Compile (t,pl) ->
195
      Format.fprintf ppf "[DEBUG:compile]@.";
196 197
      let t = Typer.typ tenv t
      and pl = List.map (Typer.pat tenv) pl in
198 199
      Patterns.Compile.debug_compile ppf t pl
(*
200
       Patterns.demo_compile ppf (Types.descr t) (List.map Patterns.descr pl)  
201
*)
202
  | `Explain (t0,t,e) ->
203
      Format.fprintf ppf "[DEBUG:explain]@.";
204 205 206
      let t = Types.descr (Typer.typ tenv t) in
      let t0 = Types.descr (Typer.typ tenv t0) in
      (match Explain.explain t0 t (eval_quiet tenv cenv e) with
207
	 | Some p ->
208
	     Format.fprintf ppf "%a@."
209
	       Explain.print p
210
	 | None ->
211
	     Format.fprintf ppf "Value has given type@.")
212 213 214 215 216 217 218 219 220 221 222 223 224
  | `Single t ->
      Format.fprintf ppf "[DEBUG:single]@.";
      let t = Typer.typ tenv t in
      (try 
	 let c = Sample.single (Types.descr t) in
	 Format.fprintf ppf "Constant:%a@." Types.Print.print_const c
       with
	 | Exit -> Format.fprintf ppf "Non constant@." 
	 | Not_found ->  Format.fprintf ppf "Empty@.")
  | `Approx (p,t) ->
      Format.fprintf ppf "[DEBUG:approx]@.";
      let t = Typer.typ tenv t in
      let p = Typer.pat tenv p in
225
      Patterns.demo ppf (Patterns.descr p) (Types.descr t); 
226
(*
227 228 229
      let (x,c) = Patterns.approx (Patterns.descr p) (Types.descr t) in
      List.iter (fun x -> Format.fprintf ppf "%a=* " U.print (Id.value x)) x;
      List.iter
230
	(fun (x,c) ->
231 232 233
	   Format.fprintf ppf "%a=%a "
	     U.print (Id.value x) 
	     Types.Print.print_const c
234
	) c;  *)
235
      Format.fprintf ppf "@."
236

237 238 239 240 241 242 243 244 245 246
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
  | `Print_schema schema ->
247 248 249
      let uri = Typer.find_schema schema tenv in
      let sch = Typer.get_schema uri in
      Schema_common.print_schema ppf sch;
250 251 252 253 254
      flush_ppf ppf
  | `Print_type name ->
      Typer.dump_type ppf tenv name;
      flush_ppf ppf
  | `Print_schema_type schema_ref ->
255
      Typer.dump_schema_type ppf tenv schema_ref;
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
      flush_ppf ppf
  | `Reinit_ns ->
      Typer.set_ns_table_for_printer tenv
  | `Help ->
      directive_help ppf
  | `Dump pexpr ->
      Value.dump_xml ppf (eval_quiet tenv cenv pexpr);
      flush_ppf ppf

let print_id_opt ppf = function
  | None -> Format.fprintf ppf "-"
  | Some id -> Format.fprintf ppf "val %a" U.print (Id.value id)

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

let show ppf id t v =
  Format.fprintf ppf "@[%a : @[%a%a@]@]@."
    print_id_opt id 
    print_norm t 
    print_value_opt v

let phrases ppf phs =
  let (tenv,cenv,_) = 
    Compile.comp_unit 
      ~run:true ~show:(show ppf) 
283
      ~loading:Librarian.import_and_run
284 285 286 287
      ~directive:(directive ppf)
      !typing_env !compile_env phs in
  typing_env := tenv;
  compile_env := cenv
288

289 290 291
let catch_exn ppf_err exn =
  if not catch_exceptions then raise exn;
  match exn with
292 293 294 295 296 297 298 299
  | (End_of_file | Failure _ | Not_found | Invalid_argument _ | Sys.Break) 
      as e -> 
      raise e
  | exn -> 
      print_exn ppf_err exn;
      Format.fprintf ppf_err "@."

let parse rule input =
300 301
  try Parser.localize_exn (fun () -> rule input)
  with e -> Parser.sync (); raise e
302

303
let run rule ppf ppf_err input =
304
  try phrases ppf (parse rule input); true
305
  with Escape exn -> raise exn | exn -> catch_exn ppf_err exn; false
306

307
let topinput = run Parser.top_phrases
308
let script = run Parser.prog
309

310

311
let compile src out_dir =
312
  try
313 314 315 316 317 318 319 320 321
    if not (Filename.check_suffix src ".cd")
    then raise (InvalidInputFilename src);
    let cu = Filename.chop_suffix (Filename.basename src) ".cd" in
    let out_dir = 
      match out_dir with
	| None -> Filename.dirname src
	| Some x -> x in
    let out = Filename.concat out_dir (cu ^ ".cdo") in
    let id = Types.CompUnit.mk (U.mk_latin1 cu) in
322
    Librarian.compile !verbose cu id src;
323
    Librarian.save cu id out;
324
    exit 0
325 326
  with exn -> catch_exn Format.err_formatter exn; exit 1
  
327
let compile_run src =
328
  try
329 330 331 332 333 334
    let cu = 
      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
335
    let id = Types.CompUnit.mk (U.mk_latin1 cu) in
336
    Librarian.compile !verbose cu id src;
337
    Librarian.run id
338
  with exn -> catch_exn Format.err_formatter exn; exit 1
339

340
let run obj =  
341
  try
342
    if not (Filename.check_suffix obj ".cdo") || (Filename.basename obj <> obj)
343 344 345
    then raise (InvalidObjectFilename obj);
    let cu = Filename.chop_suffix (Filename.basename obj) ".cdo" in
    let id = Types.CompUnit.mk (U.mk_latin1 cu) in
346
    Librarian.import_and_run id
347
  with exn -> catch_exn Format.err_formatter exn; exit 1
348

349 350

let dump_env ppf = dump_env ppf !typing_env !compile_env
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

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
      | Some id, Some v -> 
	  let id = Id.value id in
	  vals := (Some id,v) :: !vals
      | None, Some v ->
	  vals := (None,v) :: !vals
      | _ -> assert false
  in
  let r () = 
    ignore (Compile.comp_unit 
	      ~run:true ~show Builtin.env Compile.empty_toplevel phs) in
  Eval.new_stack r ();
  List.rev !vals
  
let eval s =
  try eval s
  with exn -> 
    let b = Buffer.create 1024 in
    let ppf = Format.formatter_of_buffer b in
    print_exn ppf exn;
    Format.fprintf ppf "@.";
378
    Value.failwith' (Buffer.contents b)
379
	   
380

381
let () =        
382
  Operators.register_fun "eval_expr" Builtin_defs.string_latin1 Types.any
383 384 385 386 387
  (fun v ->
     match eval (Value.cduce2ocaml_string v) with
       | [ (None,v) ] -> v
       | _ -> Value.failwith' "eval: the string must evaluate to a single value"
  )
388