Blame compiler/maincompile.ml

Packit bd2e5d
(***********************************************************************)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(*                 MLTk, Tcl/Tk interface of OCaml                     *)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(*    Francois Rouaix, Francois Pessaux, Jun Furuse and Pierre Weis    *)
Packit bd2e5d
(*               projet Cristal, INRIA Rocquencourt                    *)
Packit bd2e5d
(*            Jacques Garrigue, Kyoto University RIMS                  *)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(*  Copyright 2002 Institut National de Recherche en Informatique et   *)
Packit bd2e5d
(*  en Automatique and Kyoto University.  All rights reserved.         *)
Packit bd2e5d
(*  This file is distributed under the terms of the GNU Library        *)
Packit bd2e5d
(*  General Public License, with the special exception on linking      *)
Packit bd2e5d
(*  described in file LICENSE found in the OCaml source tree.          *)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(***********************************************************************)
Packit bd2e5d
Packit bd2e5d
(* $Id$ *)
Packit bd2e5d
Packit bd2e5d
open StdLabels
Packit bd2e5d
open Tables
Packit bd2e5d
open Printer
Packit bd2e5d
open Compile
Packit bd2e5d
open Intf
Packit bd2e5d
Packit bd2e5d
let flag_verbose = ref false
Packit bd2e5d
let verbose_string s =
Packit bd2e5d
  if !flag_verbose then prerr_string s
Packit bd2e5d
let verbose_endline s =
Packit bd2e5d
  if !flag_verbose then prerr_endline s
Packit bd2e5d
Packit bd2e5d
let input_name = ref "Widgets.src"
Packit bd2e5d
let output_dir = ref ""
Packit bd2e5d
let destfile f = Filename.concat !output_dir f
Packit bd2e5d
Packit bd2e5d
let usage () =
Packit bd2e5d
  prerr_string "Usage: tkcompiler input.src\n";
Packit bd2e5d
  flush stderr;
Packit bd2e5d
  exit 1
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
let prerr_error_header () =
Packit bd2e5d
  prerr_string "File \""; prerr_string !input_name;
Packit bd2e5d
  prerr_string "\", line ";
Packit bd2e5d
  prerr_string (string_of_int !Lexer.current_line);
Packit bd2e5d
  prerr_string ": "
Packit bd2e5d
Packit bd2e5d
(* parse Widget.src config file *)
Packit bd2e5d
let parse_file filename =
Packit bd2e5d
  let ic = open_in_bin filename in
Packit bd2e5d
  let lexbuf =
Packit bd2e5d
    try
Packit bd2e5d
      let code_list = Ppparse.parse_channel ic in
Packit bd2e5d
      close_in ic;
Packit bd2e5d
      let buf = Buffer.create 50000 in
Packit bd2e5d
      List.iter (Ppexec.exec
Packit bd2e5d
                   (fun l -> Buffer.add_string buf
Packit bd2e5d
                       (Printf.sprintf "##line %d\n" l))
Packit bd2e5d
                   (Buffer.add_string buf))
Packit bd2e5d
        (if !Flags.camltk then Code.Define "CAMLTK" :: code_list
Packit bd2e5d
        else code_list);
Packit bd2e5d
      Lexing.from_string (Buffer.contents buf)
Packit bd2e5d
    with
Packit bd2e5d
    | Ppparse.Error s ->
Packit bd2e5d
        close_in ic;
Packit bd2e5d
        raise (Compiler_Error (Printf.sprintf "Preprocess error: %s" s))
Packit bd2e5d
  in
Packit bd2e5d
  try
Packit bd2e5d
    while true do
Packit bd2e5d
      Parser.entry Lexer.main lexbuf
Packit bd2e5d
    done
Packit bd2e5d
  with
Packit bd2e5d
  | Parsing.Parse_error ->
Packit bd2e5d
      prerr_error_header();
Packit bd2e5d
      prerr_string "Syntax error \n";
Packit bd2e5d
      exit 1
Packit bd2e5d
  | Lexer.Lexical_error s ->
Packit bd2e5d
      prerr_error_header();
Packit bd2e5d
      prerr_string "Lexical error (";
Packit bd2e5d
      prerr_string s;
Packit bd2e5d
      prerr_string ")\n";
Packit bd2e5d
      exit 1
Packit bd2e5d
  | Duplicate_Definition (s,s') ->
Packit bd2e5d
      prerr_error_header();
Packit bd2e5d
      prerr_string s; prerr_string " "; prerr_string s';
Packit bd2e5d
      prerr_string " is defined twice.\n";
Packit bd2e5d
      exit 1
Packit bd2e5d
  | Compiler_Error s ->
Packit bd2e5d
      prerr_error_header();
Packit bd2e5d
      prerr_string "Internal error: "; prerr_string s; prerr_string "\n";
Packit bd2e5d
      prerr_string "Please report bug\n";
Packit bd2e5d
      exit 1
Packit bd2e5d
  | End_of_file ->
Packit bd2e5d
      ()
Packit bd2e5d
Packit bd2e5d
(* The hack to provoke the production of cCAMLtoTKoptions_constrs *)
Packit bd2e5d
Packit bd2e5d
(* Auxiliary function: the list of all the elements associated to keys
Packit bd2e5d
   in an hash table. *)
Packit bd2e5d
let elements t =
Packit bd2e5d
 let elems = ref [] in
Packit bd2e5d
 Hashtbl.iter (fun _ d -> elems := d :: !elems) t;
Packit bd2e5d
 !elems;;
Packit bd2e5d
Packit bd2e5d
(* Verifies that duplicated clauses are semantically equivalent and
Packit bd2e5d
   returns a unique set of clauses. *)
Packit bd2e5d
let uniq_clauses = function
Packit bd2e5d
  | [] -> []
Packit bd2e5d
  | l ->
Packit bd2e5d
     let check_constr constr1 constr2 =
Packit bd2e5d
       if constr1.template <> constr2.template then
Packit bd2e5d
       begin
Packit bd2e5d
        let code1, vars11, vars12, opts1 =
Packit bd2e5d
         code_of_template ~context_widget:"dummy" constr1.template in
Packit bd2e5d
        let code2, vars12, vars22, opts2 =
Packit bd2e5d
         code_of_template ~context_widget:"dummy" constr2.template in
Packit bd2e5d
        let err =
Packit bd2e5d
         Printf.sprintf
Packit bd2e5d
          "uncompatible redondant clauses for variant %s:\n %s\n and\n %s"
Packit bd2e5d
          constr1.var_name code1 code2 in
Packit bd2e5d
        Format.print_newline();
Packit bd2e5d
        print_fullcomponent constr1;
Packit bd2e5d
        Format.print_newline();
Packit bd2e5d
        print_fullcomponent constr2;
Packit bd2e5d
        Format.print_newline();
Packit bd2e5d
        prerr_endline err;
Packit bd2e5d
        fatal_error err
Packit bd2e5d
       end in
Packit bd2e5d
     let t = Hashtbl.create 11 in
Packit bd2e5d
     List.iter l
Packit bd2e5d
      ~f:(fun constr ->
Packit bd2e5d
       let c = constr.var_name in
Packit bd2e5d
       if Hashtbl.mem t c
Packit bd2e5d
       then (check_constr constr (Hashtbl.find t c))
Packit bd2e5d
       else Hashtbl.add t c constr);
Packit bd2e5d
     elements t;;
Packit bd2e5d
Packit bd2e5d
let option_hack oc =
Packit bd2e5d
  if Hashtbl.mem types_table "options" then
Packit bd2e5d
   let typdef = Hashtbl.find types_table "options" in
Packit bd2e5d
   let hack =
Packit bd2e5d
     { parser_arity = OneToken;
Packit bd2e5d
       constructors = begin
Packit bd2e5d
         let constrs =
Packit bd2e5d
           List.map typdef.constructors ~f:
Packit bd2e5d
             begin fun c ->
Packit bd2e5d
               { component = Constructor;
Packit bd2e5d
                 ml_name = (if !Flags.camltk then "C" ^ c.ml_name
Packit bd2e5d
                            else c.ml_name);
Packit bd2e5d
                 var_name = c.var_name; (* as variants *)
Packit bd2e5d
                 template =
Packit bd2e5d
                 begin match c.template with
Packit bd2e5d
                   ListArg (x :: _) -> x
Packit bd2e5d
                 | _ -> fatal_error "bogus hack"
Packit bd2e5d
                 end;
Packit bd2e5d
                 result = UserDefined "options_constrs";
Packit bd2e5d
                 safe = true }
Packit bd2e5d
             end in
Packit bd2e5d
         if !Flags.camltk then constrs else uniq_clauses constrs (* JPF ?? *)
Packit bd2e5d
       end;
Packit bd2e5d
       subtypes = [];
Packit bd2e5d
       requires_widget_context = false;
Packit bd2e5d
       variant = false }
Packit bd2e5d
   in
Packit bd2e5d
   write_CAMLtoTK
Packit bd2e5d
     ~w:(output_string oc) ~def:hack ~safetype:false "options_constrs"
Packit bd2e5d
Packit bd2e5d
let realname name =
Packit bd2e5d
  (* module name fix for camltk *)
Packit bd2e5d
  let name = caml_name name in
Packit bd2e5d
  if !Flags.camltk then "c" ^ String.capitalize_ascii name
Packit bd2e5d
  else name
Packit bd2e5d
;;
Packit bd2e5d
Packit bd2e5d
(* analize the parsed Widget.src and output source files *)
Packit bd2e5d
let compile () =
Packit bd2e5d
  verbose_endline "Creating _tkgen.ml ...";
Packit bd2e5d
  let oc = open_out_bin (destfile "_tkgen.ml") in
Packit bd2e5d
  let oc' = open_out_bin (destfile "_tkigen.ml") in
Packit bd2e5d
  let oc'' = open_out_bin (destfile "_tkfgen.ml") in
Packit bd2e5d
  let sorted_types = Tsort.sort types_order in
Packit bd2e5d
  verbose_endline "  writing types ...";
Packit bd2e5d
  List.iter sorted_types ~f:
Packit bd2e5d
  begin fun typname ->
Packit bd2e5d
  verbose_string ("    " ^ typname ^ " ");
Packit bd2e5d
  try
Packit bd2e5d
    let typdef = Hashtbl.find types_table typname in
Packit bd2e5d
    verbose_string "type ";
Packit bd2e5d
    write_type ~intf:(output_string oc)
Packit bd2e5d
               ~impl:(output_string oc')
Packit bd2e5d
               typname ~def:typdef;
Packit bd2e5d
    verbose_string "C2T ";
Packit bd2e5d
    write_CAMLtoTK ~w:(output_string oc') typname ~def:typdef;
Packit bd2e5d
    verbose_string "T2C ";
Packit bd2e5d
    if List.mem typname !types_returned then
Packit bd2e5d
    write_TKtoCAML ~w:(output_string oc') typname ~def:typdef;
Packit bd2e5d
    verbose_string "CO ";
Packit bd2e5d
    if not !Flags.camltk then (* only for LablTk *)
Packit bd2e5d
      write_catch_optionals ~w:(output_string oc') typname ~def:typdef;
Packit bd2e5d
    verbose_endline "."
Packit bd2e5d
  with Not_found ->
Packit bd2e5d
    if not (List.mem_assoc typname !types_external) then
Packit bd2e5d
    begin
Packit bd2e5d
      verbose_string "Type ";
Packit bd2e5d
      verbose_string typname;
Packit bd2e5d
      verbose_string " is undeclared external or undefined\n"
Packit bd2e5d
    end
Packit bd2e5d
    else verbose_endline "."
Packit bd2e5d
  end;
Packit bd2e5d
  verbose_endline "  option hacking ...";
Packit bd2e5d
  option_hack oc';
Packit bd2e5d
  verbose_endline "  writing functions ...";
Packit bd2e5d
  List.iter ~f:(write_function ~w:(output_string oc'')) !function_table;
Packit bd2e5d
  close_out oc;
Packit bd2e5d
  close_out oc';
Packit bd2e5d
  close_out oc'';
Packit bd2e5d
  (* Write the interface for public functions *)
Packit bd2e5d
  (* this interface is used only for documentation *)
Packit bd2e5d
  verbose_endline "Creating _tkgen.mli ...";
Packit bd2e5d
  let oc = open_out_bin (destfile "_tkgen.mli") in
Packit bd2e5d
  List.iter (sort_components !function_table)
Packit bd2e5d
    ~f:(write_function_type ~w:(output_string oc));
Packit bd2e5d
  close_out oc;
Packit bd2e5d
  verbose_endline "Creating other ml, mli ...";
Packit bd2e5d
  let write_module wname wdef =
Packit bd2e5d
    verbose_endline ("  "^wname);
Packit bd2e5d
    let modname = realname wname in
Packit bd2e5d
    let oc = open_out_bin (destfile (modname ^ ".ml"))
Packit bd2e5d
    and oc' = open_out_bin (destfile (modname ^ ".mli")) in
Packit bd2e5d
    Copyright.write ~w:(output_string oc);
Packit bd2e5d
    Copyright.write ~w:(output_string oc');
Packit bd2e5d
    begin match wdef.module_type with
Packit bd2e5d
      Widget -> output_string oc' ("(** The "^wname^" widget *)\n")
Packit bd2e5d
    | Family -> output_string oc' ("(** The "^wname^" commands  *)\n")
Packit bd2e5d
    end;
Packit bd2e5d
    List.iter ~f:(fun s -> output_string oc s; output_string oc' s)
Packit bd2e5d
      begin
Packit bd2e5d
        if !Flags.camltk then
Packit bd2e5d
          [ "open CTk\n";
Packit bd2e5d
            "open Tkintf\n";
Packit bd2e5d
            "open Widget\n";
Packit bd2e5d
            "open Textvariable\n\n" ]
Packit bd2e5d
        else
Packit bd2e5d
          [ "open StdLabels\n";
Packit bd2e5d
            "open Tk\n";
Packit bd2e5d
            "open Tkintf\n";
Packit bd2e5d
            "open Widget\n";
Packit bd2e5d
            "open Textvariable\n\n" ]
Packit bd2e5d
      end;
Packit bd2e5d
    output_string oc "open Protocol\n";
Packit bd2e5d
    begin match wdef.module_type with
Packit bd2e5d
      Widget ->
Packit bd2e5d
        if !Flags.camltk then begin
Packit bd2e5d
          camltk_write_create ~w:(output_string oc) wname;
Packit bd2e5d
          camltk_write_named_create ~w:(output_string oc) wname;
Packit bd2e5d
          camltk_write_create_p ~w:(output_string oc') wname;
Packit bd2e5d
          camltk_write_named_create_p ~w:(output_string oc') wname;
Packit bd2e5d
        end else begin
Packit bd2e5d
          labltk_write_create ~w:(output_string oc) wname;
Packit bd2e5d
          labltk_write_create_p ~w:(output_string oc') wname
Packit bd2e5d
        end
Packit bd2e5d
    | Family -> ()
Packit bd2e5d
    end;
Packit bd2e5d
    List.iter ~f:(write_function ~w:(output_string oc))
Packit bd2e5d
          (sort_components wdef.commands);
Packit bd2e5d
    List.iter ~f:(write_function_type ~w:(output_string oc'))
Packit bd2e5d
          (sort_components wdef.commands);
Packit bd2e5d
    List.iter ~f:(write_external ~w:(output_string oc))
Packit bd2e5d
           (sort_components wdef.externals);
Packit bd2e5d
    List.iter ~f:(write_external_type ~w:(output_string oc'))
Packit bd2e5d
           (sort_components wdef.externals);
Packit bd2e5d
    close_out oc;
Packit bd2e5d
    close_out oc'
Packit bd2e5d
  in Hashtbl.iter write_module module_table;
Packit bd2e5d
Packit bd2e5d
  (* wrapper code camltk.ml and labltk.ml *)
Packit bd2e5d
  if !Flags.camltk then begin
Packit bd2e5d
    let oc = open_out_bin (destfile "camltk.ml") in
Packit bd2e5d
    Copyright.write ~w:(output_string oc);
Packit bd2e5d
    output_string oc
Packit bd2e5d
"(** This module Camltk provides the module name spaces of the CamlTk API.\n\
Packit bd2e5d
\n\
Packit bd2e5d
  The users of the CamlTk API should open this module first to access\n\
Packit bd2e5d
  the types, functions and modules of the CamlTk API easier.\n\
Packit bd2e5d
  For the documentation of each sub modules such as [Button] and [Toplevel],\n\
Packit bd2e5d
  refer to its defintion file,  [cButton.mli], [cToplevel.mli], etc.\n\
Packit bd2e5d
 *)\n\
Packit bd2e5d
\n\
Packit bd2e5d
";
Packit bd2e5d
    output_string oc "include CTk\n";
Packit bd2e5d
    output_string oc "module Tk = CTk\n";
Packit bd2e5d
    Hashtbl.iter (fun name _ ->
Packit bd2e5d
      let cname = realname name in
Packit bd2e5d
      output_string oc (Printf.sprintf "module %s = %s;;\n"
Packit bd2e5d
                          (String.capitalize_ascii (caml_name name))
Packit bd2e5d
                          (String.capitalize_ascii cname))) module_table;
Packit bd2e5d
    close_out oc
Packit bd2e5d
  end else begin
Packit bd2e5d
    let oc = open_out_bin (destfile "labltk.ml") in
Packit bd2e5d
    Copyright.write ~w:(output_string oc);
Packit bd2e5d
    output_string oc
Packit bd2e5d
"(** This module Labltk provides the module name spaces of the LablTk API,\n\
Packit bd2e5d
  useful to call LablTk functions inside CamlTk programs. 100% LablTk users\n\
Packit bd2e5d
  do not need to use this. *)\n\
Packit bd2e5d
\n\
Packit bd2e5d
";
Packit bd2e5d
    output_string oc "module Widget = Widget;;\n\
Packit bd2e5d
module Protocol = Protocol;;\n\
Packit bd2e5d
module Textvariable = Textvariable;;\n\
Packit bd2e5d
module Fileevent = Fileevent;;\n\
Packit bd2e5d
module Timer = Timer;;\n\
Packit bd2e5d
";
Packit bd2e5d
    Hashtbl.iter (fun name _ ->
Packit bd2e5d
      let cname = realname name in
Packit bd2e5d
      output_string oc (Printf.sprintf "module %s = %s;;\n"
Packit bd2e5d
                          (String.capitalize_ascii (caml_name name))
Packit bd2e5d
                          (String.capitalize_ascii cname))) module_table;
Packit bd2e5d
    (* widget typer *)
Packit bd2e5d
    output_string oc "\n(** Widget typers *)\n\nopen Widget\n\n";
Packit bd2e5d
    Hashtbl.iter (fun name def ->
Packit bd2e5d
      match def.module_type with
Packit bd2e5d
      | Widget ->
Packit bd2e5d
          let name = caml_name name in
Packit bd2e5d
          output_string oc (Printf.sprintf
Packit bd2e5d
              "let %s (w : any widget) =\n" name);
Packit bd2e5d
          output_string oc (Printf.sprintf
Packit bd2e5d
              "  Rawwidget.check_class w widget_%s_table;\n" name);
Packit bd2e5d
          output_string oc (Printf.sprintf
Packit bd2e5d
              "  (Obj.magic w : %s widget);;\n\n" name);
Packit bd2e5d
      | _ -> () ) module_table;
Packit bd2e5d
    close_out oc
Packit bd2e5d
  end;
Packit bd2e5d
Packit bd2e5d
  (* write the module list for the Makefile *)
Packit bd2e5d
  (* and hack to death until it works *)
Packit bd2e5d
  let oc = open_out_bin (destfile "modules") in
Packit bd2e5d
  if !Flags.camltk then output_string oc "CWIDGETOBJS="
Packit bd2e5d
  else output_string oc "WIDGETOBJS=";
Packit bd2e5d
  Hashtbl.iter
Packit bd2e5d
    (fun name _ ->
Packit bd2e5d
      let name = realname name in
Packit bd2e5d
      output_string oc " ";
Packit bd2e5d
      output_string oc name;
Packit bd2e5d
      output_string oc ".cmo")
Packit bd2e5d
    module_table;
Packit bd2e5d
  output_string oc "\n";
Packit bd2e5d
  Hashtbl.iter
Packit bd2e5d
    (fun name _ ->
Packit bd2e5d
      let name = realname name in
Packit bd2e5d
      output_string oc name;
Packit bd2e5d
      output_string oc ".ml ")
Packit bd2e5d
    module_table;
Packit bd2e5d
  output_string oc ": _tkgen.ml\n\n";
Packit bd2e5d
  Hashtbl.iter
Packit bd2e5d
    (fun name _ ->
Packit bd2e5d
      let name = realname name in
Packit bd2e5d
      output_string oc name;
Packit bd2e5d
      output_string oc ".cmo : ";
Packit bd2e5d
      output_string oc name;
Packit bd2e5d
      output_string oc ".ml\n";
Packit bd2e5d
      output_string oc name;
Packit bd2e5d
      output_string oc ".cmi : ";
Packit bd2e5d
      output_string oc name;
Packit bd2e5d
      output_string oc ".mli\n")
Packit bd2e5d
    module_table;
Packit bd2e5d
Packit bd2e5d
  (* for camltk.ml wrapper *)
Packit bd2e5d
  if !Flags.camltk then begin
Packit bd2e5d
    output_string oc "camltk.cmo : cTk.cmo ";
Packit bd2e5d
    Hashtbl.iter
Packit bd2e5d
      (fun name _ ->
Packit bd2e5d
        let name = realname name in
Packit bd2e5d
        output_string oc name;
Packit bd2e5d
        output_string oc ".cmo ") module_table;
Packit bd2e5d
    output_string oc "\n"
Packit bd2e5d
  end;
Packit bd2e5d
  close_out oc
Packit bd2e5d
Packit bd2e5d
let main () =
Packit bd2e5d
  Arg.parse
Packit bd2e5d
    [ "-verbose",  Arg.Unit (fun () -> flag_verbose := true),
Packit bd2e5d
      "Make output verbose";
Packit bd2e5d
      "-camltk", Arg.Unit (fun () -> Flags.camltk := true),
Packit bd2e5d
      "Make CamlTk interface";
Packit bd2e5d
      "-outdir", Arg.String (fun s -> output_dir := s),
Packit bd2e5d
      "output directory";
Packit bd2e5d
      "-debugpp", Arg.Unit (fun () -> Ppexec.debug := true),
Packit bd2e5d
      "debug preprocessor"
Packit bd2e5d
    ]
Packit bd2e5d
    (fun filename -> input_name := filename)
Packit bd2e5d
    "Usage: tkcompiler <source file>" ;
Packit bd2e5d
  if !output_dir = "" then begin
Packit bd2e5d
    prerr_endline "specify -outdir option";
Packit bd2e5d
    exit 1
Packit bd2e5d
  end;
Packit bd2e5d
  try
Packit bd2e5d
    verbose_endline "Parsing...";
Packit bd2e5d
    parse_file !input_name;
Packit bd2e5d
    verbose_endline "Compiling...";
Packit bd2e5d
    compile ();
Packit bd2e5d
    verbose_endline "Finished";
Packit bd2e5d
    exit 0
Packit bd2e5d
  with
Packit bd2e5d
  | Lexer.Lexical_error s ->
Packit bd2e5d
      prerr_string "Invalid lexical character: ";
Packit bd2e5d
      prerr_endline s;
Packit bd2e5d
      exit 1
Packit bd2e5d
  | Duplicate_Definition (s, s') ->
Packit bd2e5d
      prerr_string s; prerr_string " "; prerr_string s';
Packit bd2e5d
      prerr_endline " is redefined illegally";
Packit bd2e5d
      exit 1
Packit bd2e5d
  | Invalid_implicit_constructor c ->
Packit bd2e5d
      prerr_string "Constructor ";
Packit bd2e5d
      prerr_string c;
Packit bd2e5d
      prerr_endline " is used implicitly before defined";
Packit bd2e5d
      exit 1
Packit bd2e5d
  | Tsort.Cyclic ->
Packit bd2e5d
      prerr_endline "Cyclic dependency of types";
Packit bd2e5d
      exit 1
Packit bd2e5d
Packit bd2e5d
let () = Printexc.catch main ()