Blame compiler/tables.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
Packit bd2e5d
(* Internal compiler errors *)
Packit bd2e5d
Packit bd2e5d
exception Compiler_Error of string
Packit bd2e5d
let fatal_error s = raise (Compiler_Error s)
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
(* Types of the description language *)
Packit bd2e5d
type mltype =
Packit bd2e5d
   Unit
Packit bd2e5d
 | Int
Packit bd2e5d
 | Float
Packit bd2e5d
 | Bool
Packit bd2e5d
 | Char
Packit bd2e5d
 | String
Packit bd2e5d
 | List of mltype
Packit bd2e5d
 | Product of mltype list
Packit bd2e5d
 | Record of (string * mltype) list
Packit bd2e5d
 | UserDefined of string
Packit bd2e5d
 | Subtype of string * string
Packit bd2e5d
 | Function of mltype                   (* arg type only *)
Packit bd2e5d
 | As of mltype * string
Packit bd2e5d
Packit bd2e5d
type template =
Packit bd2e5d
   StringArg of string
Packit bd2e5d
 | TypeArg of string * mltype
Packit bd2e5d
 | ListArg of template list
Packit bd2e5d
 | OptionalArgs of string * template list * template list
Packit bd2e5d
Packit bd2e5d
(* Sorts of components *)
Packit bd2e5d
type component_type =
Packit bd2e5d
   Constructor
Packit bd2e5d
 | Command
Packit bd2e5d
 | External
Packit bd2e5d
Packit bd2e5d
(* Full definition of a component *)
Packit bd2e5d
type fullcomponent = {
Packit bd2e5d
  component : component_type;
Packit bd2e5d
  ml_name : string; (* used for camltk *)
Packit bd2e5d
  var_name : string; (* used just for labltk *)
Packit bd2e5d
  template : template;
Packit bd2e5d
  result   : mltype;
Packit bd2e5d
  safe : bool
Packit bd2e5d
  }
Packit bd2e5d
Packit bd2e5d
let sort_components =
Packit bd2e5d
  List.sort ~cmp:(fun c1 c2 ->  compare c1.ml_name c2.ml_name)
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
(* components are given either in full or abbreviated *)
Packit bd2e5d
type component =
Packit bd2e5d
   Full of fullcomponent
Packit bd2e5d
 | Abbrev of string
Packit bd2e5d
Packit bd2e5d
(* A type definition *)
Packit bd2e5d
(*
Packit bd2e5d
 requires_widget_context: the converter of the type MUST be passed
Packit bd2e5d
   an additional argument of type Widget.
Packit bd2e5d
*)
Packit bd2e5d
Packit bd2e5d
type parser_arity =
Packit bd2e5d
  OneToken
Packit bd2e5d
| MultipleToken
Packit bd2e5d
Packit bd2e5d
type type_def = {
Packit bd2e5d
  parser_arity : parser_arity;
Packit bd2e5d
  mutable constructors : fullcomponent list;
Packit bd2e5d
  mutable subtypes : (string * fullcomponent list) list;
Packit bd2e5d
  mutable requires_widget_context : bool;
Packit bd2e5d
  mutable variant : bool
Packit bd2e5d
}
Packit bd2e5d
Packit bd2e5d
type module_type =
Packit bd2e5d
    Widget
Packit bd2e5d
  | Family
Packit bd2e5d
Packit bd2e5d
type module_def = {
Packit bd2e5d
  module_type : module_type;
Packit bd2e5d
  commands : fullcomponent list;
Packit bd2e5d
  externals : fullcomponent list
Packit bd2e5d
}
Packit bd2e5d
Packit bd2e5d
(******************** The tables ********************)
Packit bd2e5d
Packit bd2e5d
(* the table of all explicitly defined types *)
Packit bd2e5d
let types_table = (Hashtbl.create 37 : (string, type_def) Hashtbl.t)
Packit bd2e5d
(* "builtin" types *)
Packit bd2e5d
let types_external = ref ([] : (string * parser_arity) list)
Packit bd2e5d
(* dependancy order *)
Packit bd2e5d
let types_order = (Tsort.create () : string Tsort.porder)
Packit bd2e5d
(* Types of atomic values returned by Tk functions *)
Packit bd2e5d
let types_returned = ref ([] : string list)
Packit bd2e5d
(* Function table *)
Packit bd2e5d
let function_table = ref ([] : fullcomponent list)
Packit bd2e5d
(* Widget/Module table *)
Packit bd2e5d
let module_table = (Hashtbl.create 37 : (string, module_def) Hashtbl.t)
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
(* variant name *)
Packit bd2e5d
let rec getvarname ml_name temp =
Packit bd2e5d
  let offhypben s =
Packit bd2e5d
    if (try String.sub s ~pos:0 ~len:1 with _ -> "") = "-" then
Packit bd2e5d
      String.sub s ~pos:1 ~len:(String.length s - 1)
Packit bd2e5d
    else s
Packit bd2e5d
  in
Packit bd2e5d
    let head =  String.capitalize_ascii (offhypben begin
Packit bd2e5d
                  match temp with
Packit bd2e5d
                    StringArg s -> s
Packit bd2e5d
                  | TypeArg (s,t) -> s
Packit bd2e5d
                  | ListArg (h::_) -> getvarname ml_name h
Packit bd2e5d
                  | OptionalArgs (s,_,_) -> s
Packit bd2e5d
                  | ListArg [] -> ""
Packit bd2e5d
                end)
Packit bd2e5d
    in
Packit bd2e5d
    let varname = if head = "" then ml_name
Packit bd2e5d
                  else if head.[0] >= 'A' && head.[0] <= 'Z' then head
Packit bd2e5d
                       else ml_name
Packit bd2e5d
    in varname
Packit bd2e5d
Packit bd2e5d
(***** Some utilities on the various tables *****)
Packit bd2e5d
(* Enter a new empty type *)
Packit bd2e5d
let new_type typname arity =
Packit bd2e5d
  Tsort.add_element types_order typname;
Packit bd2e5d
  let typdef = {parser_arity = arity;
Packit bd2e5d
                constructors = [];
Packit bd2e5d
                subtypes = [];
Packit bd2e5d
                requires_widget_context = false;
Packit bd2e5d
                variant = false} in
Packit bd2e5d
    Hashtbl.add types_table typname typdef;
Packit bd2e5d
    typdef
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
(* Assume that types not yet defined are not subtyped *)
Packit bd2e5d
(* Widget is builtin and implicitly subtyped *)
Packit bd2e5d
let is_subtyped s =
Packit bd2e5d
  s = "widget" ||
Packit bd2e5d
  try
Packit bd2e5d
    let typdef = Hashtbl.find types_table s in
Packit bd2e5d
      typdef.subtypes <> []
Packit bd2e5d
  with
Packit bd2e5d
    Not_found -> false
Packit bd2e5d
Packit bd2e5d
let requires_widget_context s =
Packit bd2e5d
  try
Packit bd2e5d
    (Hashtbl.find types_table s).requires_widget_context
Packit bd2e5d
  with
Packit bd2e5d
    Not_found -> false
Packit bd2e5d
Packit bd2e5d
let declared_type_parser_arity s =
Packit bd2e5d
  try
Packit bd2e5d
    (Hashtbl.find types_table s).parser_arity
Packit bd2e5d
  with
Packit bd2e5d
    Not_found ->
Packit bd2e5d
      try List.assoc s !types_external
Packit bd2e5d
      with
Packit bd2e5d
        Not_found ->
Packit bd2e5d
           prerr_string "Type "; prerr_string s;
Packit bd2e5d
           prerr_string " is undeclared external or undefined\n";
Packit bd2e5d
           prerr_string ("Assuming cTKtoCAML"^s^" : string -> "^s^"\n");
Packit bd2e5d
           OneToken
Packit bd2e5d
Packit bd2e5d
let rec type_parser_arity = function
Packit bd2e5d
   Unit -> OneToken
Packit bd2e5d
 | Int -> OneToken
Packit bd2e5d
 | Float -> OneToken
Packit bd2e5d
 | Bool -> OneToken
Packit bd2e5d
 | Char -> OneToken
Packit bd2e5d
 | String -> OneToken
Packit bd2e5d
 | List _ -> MultipleToken
Packit bd2e5d
 | Product _ -> MultipleToken
Packit bd2e5d
 | Record _ -> MultipleToken
Packit bd2e5d
 | UserDefined s -> declared_type_parser_arity s
Packit bd2e5d
 | Subtype (s,_) -> declared_type_parser_arity s
Packit bd2e5d
 | Function _ -> OneToken
Packit bd2e5d
 | As (ty, _) -> type_parser_arity ty
Packit bd2e5d
Packit bd2e5d
let enter_external_type s v =
Packit bd2e5d
  types_external := (s,v)::!types_external
Packit bd2e5d
Packit bd2e5d
(*** Stuff for topological Sort.list of types ***)
Packit bd2e5d
(* Make sure all types used in commands and functions are in *)
Packit bd2e5d
(* the table *)
Packit bd2e5d
let rec enter_argtype = function
Packit bd2e5d
    Unit | Int | Float | Bool | Char | String -> ()
Packit bd2e5d
  | List ty -> enter_argtype ty
Packit bd2e5d
  | Product tyl -> List.iter ~f:enter_argtype tyl
Packit bd2e5d
  | Record tyl -> List.iter tyl ~f:(fun (l,t) -> enter_argtype t)
Packit bd2e5d
  | UserDefined s -> Tsort.add_element types_order s
Packit bd2e5d
  | Subtype (s,_) -> Tsort.add_element types_order s
Packit bd2e5d
  | Function ty -> enter_argtype ty
Packit bd2e5d
  | As (ty, _) -> enter_argtype ty
Packit bd2e5d
Packit bd2e5d
let rec enter_template_types = function
Packit bd2e5d
     StringArg _ -> ()
Packit bd2e5d
   | TypeArg (l,t) -> enter_argtype t
Packit bd2e5d
   | ListArg l -> List.iter ~f:enter_template_types l
Packit bd2e5d
   | OptionalArgs (_,tl,_) -> List.iter ~f:enter_template_types tl
Packit bd2e5d
Packit bd2e5d
(* Find type dependancies on s *)
Packit bd2e5d
let rec add_dependancies s =
Packit bd2e5d
  function
Packit bd2e5d
    List ty -> add_dependancies s ty
Packit bd2e5d
  | Product tyl -> List.iter ~f:(add_dependancies s) tyl
Packit bd2e5d
  | Subtype(s',_) -> if s <> s' then Tsort.add_relation types_order (s', s)
Packit bd2e5d
  | UserDefined s' -> if s <> s' then Tsort.add_relation types_order (s', s)
Packit bd2e5d
  | Function ty -> add_dependancies s ty
Packit bd2e5d
  | As (ty, _) -> add_dependancies s ty
Packit bd2e5d
  | _ -> ()
Packit bd2e5d
Packit bd2e5d
let rec add_template_dependancies s = function
Packit bd2e5d
     StringArg _ -> ()
Packit bd2e5d
   | TypeArg (l,t) -> add_dependancies s t
Packit bd2e5d
   | ListArg l -> List.iter ~f:(add_template_dependancies s) l
Packit bd2e5d
   | OptionalArgs (_,tl,_) -> List.iter ~f:(add_template_dependancies s) tl
Packit bd2e5d
Packit bd2e5d
(* Assumes functions are not nested in products, which is reasonable due to syntax*)
Packit bd2e5d
let rec has_callback = function
Packit bd2e5d
     StringArg _ -> false
Packit bd2e5d
   | TypeArg (l,Function _ ) -> true
Packit bd2e5d
   | TypeArg _ -> false
Packit bd2e5d
   | ListArg l -> List.exists ~f:has_callback l
Packit bd2e5d
   | OptionalArgs (_,tl,_) -> List.exists ~f:has_callback tl
Packit bd2e5d
Packit bd2e5d
(*** Returned types ***)
Packit bd2e5d
let really_add ty =
Packit bd2e5d
  if List.mem ty !types_returned then ()
Packit bd2e5d
  else types_returned := ty :: !types_returned
Packit bd2e5d
Packit bd2e5d
let rec add_return_type = function
Packit bd2e5d
    Unit -> ()
Packit bd2e5d
  | Int -> ()
Packit bd2e5d
  | Float -> ()
Packit bd2e5d
  | Bool -> ()
Packit bd2e5d
  | Char -> ()
Packit bd2e5d
  | String -> ()
Packit bd2e5d
  | List ty -> add_return_type ty
Packit bd2e5d
  | Product tyl -> List.iter ~f:add_return_type tyl
Packit bd2e5d
  | Record tyl -> List.iter tyl ~f:(fun (l,t) -> add_return_type t)
Packit bd2e5d
  | UserDefined s -> really_add s
Packit bd2e5d
  | Subtype (s,_) -> really_add s
Packit bd2e5d
  | Function _ -> fatal_error "unexpected return type (function)" (* whoah *)
Packit bd2e5d
  | As (ty, _) -> add_return_type ty
Packit bd2e5d
Packit bd2e5d
(*** Update tables for a component ***)
Packit bd2e5d
let enter_component_types {template = t; result = r} =
Packit bd2e5d
  add_return_type r;
Packit bd2e5d
  enter_argtype r;
Packit bd2e5d
  enter_template_types t
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
(******************** Types and subtypes ********************)
Packit bd2e5d
exception Duplicate_Definition of string * string
Packit bd2e5d
exception Invalid_implicit_constructor of string
Packit bd2e5d
Packit bd2e5d
(* Checking duplicate definition of constructor in subtypes *)
Packit bd2e5d
let rec check_duplicate_constr allowed c =
Packit bd2e5d
  function
Packit bd2e5d
    [] -> false         (* not defined *)
Packit bd2e5d
  | c'::rest ->
Packit bd2e5d
    if c.ml_name = c'.ml_name then  (* defined *)
Packit bd2e5d
      if allowed then
Packit bd2e5d
        if c.template = c'.template then true (* same arg *)
Packit bd2e5d
        else raise (Duplicate_Definition ("constructor",c.ml_name))
Packit bd2e5d
      else raise (Duplicate_Definition ("constructor", c.ml_name))
Packit bd2e5d
    else check_duplicate_constr allowed c rest
Packit bd2e5d
Packit bd2e5d
(* Retrieve constructor *)
Packit bd2e5d
let rec find_constructor cname = function
Packit bd2e5d
   [] -> raise (Invalid_implicit_constructor cname)
Packit bd2e5d
 | c::l -> if c.ml_name = cname then c
Packit bd2e5d
           else find_constructor cname l
Packit bd2e5d
Packit bd2e5d
(* Enter a type, must not be previously defined *)
Packit bd2e5d
let enter_type typname ?(variant = false) arity constructors =
Packit bd2e5d
  if Hashtbl.mem types_table typname then
Packit bd2e5d
      raise (Duplicate_Definition ("type", typname)) else
Packit bd2e5d
  let typdef = new_type typname arity in
Packit bd2e5d
  if variant then typdef.variant <- true;
Packit bd2e5d
  List.iter constructors ~f:
Packit bd2e5d
    begin fun c ->
Packit bd2e5d
      if not (check_duplicate_constr false c typdef.constructors)
Packit bd2e5d
      then begin
Packit bd2e5d
         typdef.constructors <- c :: typdef.constructors;
Packit bd2e5d
         add_template_dependancies typname c.template
Packit bd2e5d
      end;
Packit bd2e5d
      (* Callbacks require widget context *)
Packit bd2e5d
      typdef.requires_widget_context <-
Packit bd2e5d
        typdef.requires_widget_context ||
Packit bd2e5d
                has_callback c.template
Packit bd2e5d
    end
Packit bd2e5d
Packit bd2e5d
(* Enter a subtype *)
Packit bd2e5d
let enter_subtype typ arity subtyp constructors =
Packit bd2e5d
  (* Retrieve the type if already defined, else add a new one *)
Packit bd2e5d
  let typdef =
Packit bd2e5d
    try Hashtbl.find types_table typ
Packit bd2e5d
    with Not_found -> new_type typ arity
Packit bd2e5d
  in
Packit bd2e5d
    if List.mem_assoc subtyp typdef.subtypes
Packit bd2e5d
    then raise (Duplicate_Definition ("subtype", typ ^" "^subtyp))
Packit bd2e5d
    else begin
Packit bd2e5d
      let real_constructors =
Packit bd2e5d
        List.map constructors ~f:
Packit bd2e5d
          begin function
Packit bd2e5d
            Full c ->
Packit bd2e5d
              if not (check_duplicate_constr true c typdef.constructors)
Packit bd2e5d
              then begin
Packit bd2e5d
                add_template_dependancies typ c.template;
Packit bd2e5d
                typdef.constructors <- c :: typdef.constructors
Packit bd2e5d
              end;
Packit bd2e5d
              typdef.requires_widget_context <-
Packit bd2e5d
                typdef.requires_widget_context ||
Packit bd2e5d
                has_callback c.template;
Packit bd2e5d
              c
Packit bd2e5d
          | Abbrev name -> find_constructor name typdef.constructors
Packit bd2e5d
          end
Packit bd2e5d
      in
Packit bd2e5d
       (* TODO: duplicate def in subtype are not checked *)
Packit bd2e5d
       typdef.subtypes <-
Packit bd2e5d
          (subtyp , List.sort real_constructors
Packit bd2e5d
             ~cmp:(fun c1 c2 -> compare c1.var_name c2.var_name)) ::
Packit bd2e5d
          typdef.subtypes
Packit bd2e5d
    end
Packit bd2e5d
Packit bd2e5d
(******************** Widgets ********************)
Packit bd2e5d
(* used by the parser; when enter_widget is called,
Packit bd2e5d
   all components are assumed to be in Full form *)
Packit bd2e5d
let retrieve_option optname =
Packit bd2e5d
  let optiontyp =
Packit bd2e5d
    try Hashtbl.find types_table "options"
Packit bd2e5d
    with
Packit bd2e5d
      Not_found -> raise (Invalid_implicit_constructor optname)
Packit bd2e5d
  in find_constructor optname optiontyp.constructors
Packit bd2e5d
Packit bd2e5d
(* Sort components by type *)
Packit bd2e5d
let rec add_sort l obj =
Packit bd2e5d
  match l with
Packit bd2e5d
    []  -> [obj.component ,[obj]]
Packit bd2e5d
  | (s',l)::rest ->
Packit bd2e5d
     if obj.component = s' then
Packit bd2e5d
       (s',obj::l)::rest
Packit bd2e5d
     else
Packit bd2e5d
       (s',l)::(add_sort rest obj)
Packit bd2e5d
Packit bd2e5d
let separate_components =  List.fold_left ~f:add_sort ~init:[]
Packit bd2e5d
Packit bd2e5d
let enter_widget name components =
Packit bd2e5d
  if Hashtbl.mem module_table name then
Packit bd2e5d
    raise (Duplicate_Definition ("widget/module", name)) else
Packit bd2e5d
  let sorted_components = separate_components components in
Packit bd2e5d
  List.iter sorted_components ~f:
Packit bd2e5d
    begin function
Packit bd2e5d
      Constructor, l ->
Packit bd2e5d
        enter_subtype "options" MultipleToken
Packit bd2e5d
          name (List.map ~f:(fun c -> Full c) l)
Packit bd2e5d
    | Command, l ->
Packit bd2e5d
        List.iter ~f:enter_component_types l
Packit bd2e5d
    | External, _ -> ()
Packit bd2e5d
    end;
Packit bd2e5d
  let commands =
Packit bd2e5d
      try List.assoc Command sorted_components
Packit bd2e5d
      with Not_found -> []
Packit bd2e5d
  and externals =
Packit bd2e5d
      try List.assoc External sorted_components
Packit bd2e5d
      with Not_found -> []
Packit bd2e5d
  in
Packit bd2e5d
  Hashtbl.add module_table name
Packit bd2e5d
    {module_type = Widget; commands = commands; externals = externals}
Packit bd2e5d
Packit bd2e5d
(******************** Functions ********************)
Packit bd2e5d
Packit bd2e5d
let enter_function comp =
Packit bd2e5d
  enter_component_types comp;
Packit bd2e5d
  function_table := comp :: !function_table
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
(******************** Modules ********************)
Packit bd2e5d
let enter_module name components =
Packit bd2e5d
  if Hashtbl.mem module_table name then
Packit bd2e5d
    raise (Duplicate_Definition ("widget/module", name)) else
Packit bd2e5d
  let sorted_components = separate_components components in
Packit bd2e5d
  List.iter sorted_components ~f:
Packit bd2e5d
    begin function
Packit bd2e5d
      Constructor, l -> fatal_error "unexpected Constructor"
Packit bd2e5d
    | Command, l -> List.iter ~f:enter_component_types l
Packit bd2e5d
    | External, _ -> ()
Packit bd2e5d
    end;
Packit bd2e5d
  let commands =
Packit bd2e5d
      try List.assoc Command sorted_components
Packit bd2e5d
      with Not_found -> []
Packit bd2e5d
  and externals =
Packit bd2e5d
      try List.assoc External sorted_components
Packit bd2e5d
      with Not_found -> []
Packit bd2e5d
  in
Packit bd2e5d
    Hashtbl.add module_table name
Packit bd2e5d
      {module_type = Family; commands = commands; externals = externals}