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

4 5 6
exception InvalidInputFilename of string
exception InvalidObjectFilename of string

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

11 12 13 14 15 16 17 18
(* 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

19
let quiet = ref false
20
let toplevel = ref false
21

22
let typing_env = State.ref "Cduce.typing_env" Builtin.env
23
let eval_env = State.ref "Cduce.eval_env" Eval.empty
24 25 26 27 28 29
let compile_env = State.ref "Cduce.compile_env" Compile.empty

let do_compile = ref false

let get_global_value v =
  if !do_compile 
30
  then Eval.L.var (Compile.find v !compile_env)
31
  else Eval.find_value v !eval_env
32 33 34

let get_global_type v =
  Typer.find_value v !typing_env
35

36
let enter_global_value x v t =
37
  typing_env := Typer.enter_value x t !typing_env;
38

39 40
  if !do_compile 
  then (compile_env := Compile.enter_global !compile_env x; Eval.L.push v)
41
  else eval_env := Eval.enter_value x v !eval_env
42
  
43 44 45 46 47
let rec is_abstraction = function
  | Ast.Abstraction _ -> true
  | Ast.LocatedExpr (_,e) -> is_abstraction e
  | _ -> false

48
let print_norm ppf d = 
49
  Location.protect ppf 
50
    (fun ppf -> Types.Print.print ppf ((*Types.normalize*) d))
51

52 53 54 55
let print_sample ppf s =
  Location.protect ppf
    (fun ppf -> Sample.print ppf s)

56 57 58
let print_protect ppf s =
  Location.protect ppf (fun ppf -> Format.fprintf ppf "%s" s)

59 60
let print_value ppf v =
  Location.protect ppf (fun ppf -> Value.print ppf v)
61

62 63 64 65
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

66
let dump_env ppf =
67 68
  Format.fprintf ppf "Types:%a@." Typer.dump_types !typing_env;
  Format.fprintf ppf "Namespace prefixes:@\n%a" Typer.dump_ns !typing_env;
69
  Format.fprintf ppf "Namespace prefixes used for pretty-printing:@.%t"
70
    Ns.InternalPrinter.dump;
71 72
  Format.fprintf ppf "Schemas: %s@."
    (String.concat " " (Typer.get_schema_names ()));
73
  Format.fprintf ppf "Values:@.";
74 75
  Typer.iter_values !typing_env
    (fun x t -> dump_value ppf x t (get_global_value x))
76

77 78 79
let directive_help ppf =
  Format.fprintf ppf
"Toplevel directives:
80 81 82 83 84 85 86 87
  #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>;;
88 89
"

90
let rec print_exn ppf = function
91 92
  | Location (loc, w, exn) ->
      Format.fprintf ppf "Error %a:@." Location.print_loc (loc,w);
93
      Format.fprintf ppf "%a" Location.html_hilight (loc,w); 
94
      print_exn ppf exn
95
  | Value.CDuceExn v ->
96
      Format.fprintf ppf "Uncaught CDuce exception: @[%a@]@."
97
        print_value v
98
  | Eval.MultipleDeclaration v ->
99
      Format.fprintf ppf "Multiple declaration for global value %a@." 
100
        U.print (Id.value v)
101
  | Typer.WrongLabel (t,l) ->
102
      Format.fprintf ppf "Wrong record selection; field %a " 
103
        Label.print (LabelPool.value l);
104
      Format.fprintf ppf "not present in an expression of type:@.%a@."
105
        print_norm t
106
  | Typer.ShouldHave (t,msg) ->
107
      Format.fprintf ppf "This expression should have type:@.%a@.%a@." 
108
        print_norm t
109
        print_protect msg
110
  | Typer.ShouldHave2 (t1,msg,t2) ->
111
      Format.fprintf ppf "This expression should have type:@.%a@.%a %a@." 
112
        print_norm t1
113
        print_protect msg
114
        print_norm t2
115
  | Typer.Error s ->
116
      Format.fprintf ppf "%a@." print_protect s
117
  | Typer.Constraint (s,t) ->
118
      Format.fprintf ppf "This expression should have type:@.%a@." 
119
        print_norm t;
120
      Format.fprintf ppf "but its inferred type is:@.%a@." 
121
        print_norm s;
122
      Format.fprintf ppf "which is not a subtype, as shown by the sample:@.%a@." 
123
	print_sample (Sample.get (Types.diff s t))
124
  | Typer.NonExhaustive t ->
125 126
      Format.fprintf ppf "This pattern matching is not exhaustive@.";
      Format.fprintf ppf "Residual type:@.%a@."
127
	print_norm t;
128
      Format.fprintf ppf "Sample:@.%a@." print_sample (Sample.get t)
129 130 131
  | 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 "")
132 133 134 135
  | 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
136
  | Parser.Error s | Stream.Error s -> 
137
      Format.fprintf ppf "Parsing error: %a@." print_protect s
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
  | 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:@.";
158
      Format.fprintf ppf "Object filename must have extension .cdo and no path@.";
159 160 161 162
  | Librarian.InvalidObject f ->
      Format.fprintf ppf "Invalid object file %s@." f
  | Librarian.CannotOpen f ->
      Format.fprintf ppf "Cannot open file %s@." f
163
  | Location.Generic s ->
164
      Format.fprintf ppf "%a@." print_protect s
165
  | exn ->
166
(*      raise exn *)
167
      Format.fprintf ppf "%a@." print_protect (Printexc.to_string exn)
168

169

170 171 172 173 174
let display ppf l =
  if not !quiet then
    List.iter
      (fun (x,t) -> dump_value ppf x t (get_global_value x))
      l
175 176

let eval ppf e =
177
  let (e,t) = Typer.type_expr !typing_env e in
178 179 180
  
  if not !quiet then
    Location.dump_loc ppf (e.Typed.exp_loc,`Full);
181 182 183

  let v =   
    if !do_compile then
184 185
      let e = Compile.compile_eval !compile_env e in 
      Eval.L.expr e
186 187 188 189 190 191 192
    else
      Eval.eval !eval_env e
  in
  if not !quiet then
    Format.fprintf ppf "- : @[@[%a@] =@ @[%a@]@]@." 
      print_norm t print_value v; 
  v
193
  
194
let let_decl ppf p e =
195
  let (tenv,decl,typs) = Typer.type_let_decl !typing_env p e in
196 197 198 199
  
  let () =
    if !do_compile then
      let (env,decl) = Compile.compile_let_decl !compile_env decl in
200
      Eval.L.eval decl;
201 202 203 204
      compile_env := env
    else
      eval_env := Eval.eval_let_decl !eval_env decl
  in
205
  typing_env := tenv;
206 207 208 209
  display ppf typs
  

let let_funs ppf funs =
210
  let (tenv,funs,typs) = Typer.type_let_funs !typing_env funs in
211
  
212 213 214
  let () =
    if !do_compile then
      let (env,funs) = Compile.compile_rec_funs !compile_env funs in
215
      Eval.L.eval funs;
216 217 218 219
      compile_env := env;
    else 
      eval_env := Eval.eval_rec_funs !eval_env funs 
  in
220
  typing_env := tenv;
221 222
  display ppf typs

223

224
let debug ppf = function
225
  | `Subtype (t1,t2) ->
226
      Format.fprintf ppf "[DEBUG:subtype]@.";
227 228
      let t1 = Types.descr (Typer.typ !typing_env t1)
      and t2 = Types.descr (Typer.typ !typing_env t2) in
229 230
      let s = Types.subtype t1 t2 in
      Format.fprintf ppf "%a %a %a : %b@." print_norm t1 print_protect "<=" print_norm t2 s
231
  | `Sample t ->
232 233
      Format.fprintf ppf "[DEBUG:sample]@.";
      (try
234
	 let t = Types.descr (Typer.typ !typing_env t) in
235 236 237
	 Format.fprintf ppf "%a@." print_sample (Sample.get t)
       with Not_found ->
	 Format.fprintf ppf "Empty type : no sample !@.")
238
  | `Filter (t,p) -> 
239
      Format.fprintf ppf "[DEBUG:filter]@.";
240 241
      let t = Typer.typ !typing_env t
      and p = Typer.pat !typing_env p in
242 243
      let f = Patterns.filter (Types.descr t) p in
      List.iter (fun (x,t) ->
244
		   Format.fprintf ppf " %a:%a@." U.print (Id.value x)
245 246
		     print_norm (Types.descr t)) f
  | `Accept p ->
247
      Format.fprintf ppf "[DEBUG:accept]@.";
248
      let p = Typer.pat !typing_env p in
249
      let t = Patterns.accept p in
250
      Format.fprintf ppf " %a@." Types.Print.print (Types.descr t)
251
  | `Compile (t,pl) ->
252
      Format.fprintf ppf "[DEBUG:compile]@.";
253 254
      let t = Typer.typ !typing_env t
      and pl = List.map (Typer.pat !typing_env) pl in
255
      Patterns.Compile.debug_compile ppf t pl
256 257 258 259 260 261 262 263 264
  | `Explain (t,e) ->
      Format.fprintf ppf "[DEBUG:explain]@.";
      let t = Typer.typ !typing_env t in
      (match Explain.explain (Types.descr t) (eval ppf e) with
	 | Some p ->
	     Format.fprintf ppf "Explanation: @[%a@]@." 
	       Explain.print_path p
	 | None ->
	     Format.fprintf ppf "Explanation: value has given type@.")
265

266

267 268 269
let rec collect_funs ppf accu = function
  | { descr = Ast.FunDecl e } :: rest -> collect_funs ppf (e::accu) rest
  | rest -> let_funs ppf accu; rest
270 271 272 273 274

let rec collect_types ppf accu = function
  | { descr = Ast.TypeDecl (x,t) } :: rest -> 
      collect_types ppf ((x,t) :: accu) rest
  | rest ->
275 276
      typing_env := 
        Typer.enter_types (Typer.type_defs !typing_env accu) !typing_env;
277 278
      rest

279
let flush_stdout () = Format.fprintf Format.std_formatter "@."
280

281 282 283 284 285
let rec phrases ppf phs = match phs with
  | { descr = Ast.FunDecl _ } :: _ -> 
      phrases ppf (collect_funs ppf [] phs)
  | { descr = Ast.TypeDecl (_,_) } :: _ ->
      phrases ppf (collect_types ppf [] phs)
286 287 288
  | { descr = Ast.SchemaDecl (name, schema) } :: rest ->
      Typer.register_schema name schema;
      phrases ppf rest
289
  | { descr = Ast.Namespace (pr,ns) } :: rest ->
290
      typing_env := Typer.enter_ns pr ns !typing_env;
291
      phrases ppf rest
292
  | { descr = Ast.Using (x,cu) } :: rest ->
293 294
      Librarian.import cu;
      Librarian.run Value.nil cu;
295 296
      typing_env := Typer.enter_cu x cu !typing_env;
      phrases ppf rest
297
  | { descr = Ast.EvalStatement e } :: rest ->
298
      ignore (eval ppf e);
299 300
      phrases ppf rest
  | { descr = Ast.LetDecl (p,e) } :: rest ->
301
      let_decl ppf p e;
302 303
      phrases ppf rest
  | { descr = Ast.Debug l } :: rest -> 
304
      debug ppf l; 
305
      phrases ppf rest
306 307 308 309 310 311
  | { descr = Ast.Directive `Quit } :: rest ->
      if !toplevel then raise End_of_file;
      phrases ppf rest
  | { descr = Ast.Directive `Env } :: rest ->
      dump_env ppf;
      phrases ppf rest
312 313 314 315 316 317 318 319 320 321 322 323
  | { descr = Ast.Directive (`Print_schema schema) } :: rest ->
      Schema_common.print_schema ppf (Typer.get_schema schema);
      flush_stdout ();
      phrases ppf rest
  | { descr = Ast.Directive (`Print_type name) } :: rest ->
      Typer.dump_type Format.std_formatter !typing_env name;
      flush_stdout ();
      phrases ppf rest
  | { descr = Ast.Directive (`Print_schema_type schema_ref) } :: rest ->
      Typer.dump_schema_type Format.std_formatter schema_ref;
      flush_stdout ();
      phrases ppf rest
324
  | { descr = Ast.Directive `Reinit_ns } :: rest ->
325
      Typer.set_ns_table_for_printer !typing_env;
326
      phrases ppf rest
327 328 329
  | { descr = Ast.Directive `Help } :: rest ->
      directive_help ppf;
      phrases ppf rest
330 331 332 333 334
  | { descr = Ast.Directive (`Dump pexpr) } :: rest ->
      Format.fprintf ppf "%a@."
        Value.dump_xml (Eval.eval !eval_env
          (fst (Typer.type_expr !typing_env pexpr)));
      phrases ppf rest
335 336
  | [] -> ()

337 338 339
let catch_exn ppf_err exn =
  if not catch_exceptions then raise exn;
  match exn with
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
  | (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 =
  try Some (rule input)
  with
      | Stdpp.Exc_located (_, (Location _ as e)) ->
	  Parser.sync (); raise e
      | Stdpp.Exc_located ((i,j), e) -> 
	  Parser.sync (); raise_loc i j e

355
let run rule ppf ppf_err input =
356 357 358 359
  try match parse rule input with
    | Some phs -> phrases ppf phs; true
    | None -> false
  with exn -> catch_exn ppf_err exn; false
360

361
let script = run Parser.prog
362
let topinput = run Parser.top_phrases
363

364
let compile src out_dir =
365
  try
366 367 368 369 370 371 372 373 374 375 376
    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
    Librarian.compile id src;
    Librarian.save id out;
377
    exit 0
378 379 380
  with exn -> catch_exn Format.err_formatter exn; exit 1
  
let compile_run src argv =
381
  try
382 383 384 385 386
    if not (Filename.check_suffix src ".cd")
    then raise (InvalidInputFilename src);
    let cu = Filename.chop_suffix (Filename.basename src) ".cd" in
    let id = Types.CompUnit.mk (U.mk_latin1 cu) in
    Librarian.compile id src;
387 388
    Librarian.run argv id
  with exn -> catch_exn Format.err_formatter exn; exit 1
389 390

let run obj argv =  
391
  try
392
    if not (Filename.check_suffix obj ".cdo") || (Filename.basename obj <> obj)
393 394 395
    then raise (InvalidObjectFilename obj);
    let cu = Filename.chop_suffix (Filename.basename obj) ".cdo" in
    let id = Types.CompUnit.mk (U.mk_latin1 cu) in
396 397 398
    Librarian.import id;
    Librarian.run argv id
  with exn -> catch_exn Format.err_formatter exn; exit 1
399