ocaml/otherlibs/labltk/compiler/tables.ml

424 lines
13 KiB
OCaml

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