Commit 3cdbe0b6 authored by Pietro Abate's avatar Pietro Abate
Browse files

[r2002-11-25 23:29:46 by cvscast] Empty log message

Original author: cvscast
Date: 2002-11-25 23:29:47+00:00
parent c50bc0b0
......@@ -17,6 +17,7 @@ TYPES = types/recursive.cmo \
types/recursive_share.cmo types/recursive_noshare.cmo \
types/sortedList.cmo types/sortedMap.cmo types/boolean.cmo \
types/intervals.cmo types/chars.cmo types/atoms.cmo \
types/normal.cmo \
types/types.cmo \
types/patterns.cmo \
types/sequence.cmo \
......
misc/encodings.cmo: misc/encodings.cmi
misc/encodings.cmx: misc/encodings.cmi
misc/pool.cmo: misc/pool.cmi
misc/pool.cmx: misc/pool.cmi
misc/pool.cmo: misc/state.cmi misc/pool.cmi
misc/pool.cmx: misc/state.cmx misc/pool.cmi
misc/state.cmo: misc/state.cmi
misc/state.cmx: misc/state.cmi
parser/ast.cmo: parser/location.cmi types/patterns.cmi types/types.cmi
......@@ -9,11 +9,11 @@ parser/ast.cmx: parser/location.cmx types/patterns.cmx types/types.cmx
parser/location.cmo: parser/location.cmi
parser/location.cmx: parser/location.cmi
parser/parser.cmo: parser/ast.cmo types/atoms.cmi types/chars.cmi \
types/intervals.cmi parser/lexer.cmo parser/location.cmi \
types/sequence.cmi types/types.cmi parser/wlexer.cmo parser/parser.cmi
types/intervals.cmi parser/location.cmi types/sequence.cmi \
types/types.cmi parser/wlexer.cmo parser/parser.cmi
parser/parser.cmx: parser/ast.cmx types/atoms.cmx types/chars.cmx \
types/intervals.cmx parser/lexer.cmx parser/location.cmx \
types/sequence.cmx types/types.cmx parser/wlexer.cmx parser/parser.cmi
types/intervals.cmx parser/location.cmx types/sequence.cmx \
types/types.cmx parser/wlexer.cmx parser/parser.cmi
parser/wlexer.cmo: parser/location.cmi
parser/wlexer.cmx: parser/location.cmx
typing/typed.cmo: parser/location.cmi types/patterns.cmi types/sortedMap.cmi \
......@@ -38,10 +38,14 @@ types/chars.cmo: types/chars.cmi
types/chars.cmx: types/chars.cmi
types/intervals.cmo: types/intervals.cmi
types/intervals.cmx: types/intervals.cmi
types/normal.cmo: types/normal.cmi
types/normal.cmx: types/normal.cmi
types/patterns.cmo: types/sortedList.cmi types/sortedMap.cmi misc/state.cmi \
types/types.cmi types/patterns.cmi
types/patterns.cmx: types/sortedList.cmx types/sortedMap.cmx misc/state.cmx \
types/types.cmx types/patterns.cmi
types/record.cmo: types/boolean.cmi types/sortedList.cmi
types/record.cmx: types/boolean.cmx types/sortedList.cmx
types/recursive_noshare.cmo: types/recursive.cmo misc/state.cmi
types/recursive_noshare.cmx: types/recursive.cmx misc/state.cmx
types/recursive_share.cmo: types/recursive.cmo misc/state.cmi
......@@ -62,12 +66,12 @@ types/types.cmx: types/atoms.cmx types/boolean.cmx types/chars.cmx \
types/intervals.cmx misc/pool.cmx types/recursive.cmx \
types/recursive_noshare.cmx types/sortedList.cmx types/sortedMap.cmx \
misc/state.cmx types/types.cmi
runtime/eval.cmo: runtime/load_xml.cmi runtime/print_xml.cmo \
runtime/run_dispatch.cmi misc/state.cmi typing/typed.cmo types/types.cmi \
runtime/value.cmi runtime/eval.cmi
runtime/eval.cmx: runtime/load_xml.cmx runtime/print_xml.cmx \
runtime/run_dispatch.cmx misc/state.cmx typing/typed.cmx types/types.cmx \
runtime/value.cmx runtime/eval.cmi
runtime/eval.cmo: runtime/load_xml.cmi parser/location.cmi \
runtime/print_xml.cmo runtime/run_dispatch.cmi misc/state.cmi \
typing/typed.cmo types/types.cmi runtime/value.cmi runtime/eval.cmi
runtime/eval.cmx: runtime/load_xml.cmx parser/location.cmx \
runtime/print_xml.cmx runtime/run_dispatch.cmx misc/state.cmx \
typing/typed.cmx types/types.cmx runtime/value.cmx runtime/eval.cmi
runtime/load_xml.cmo: parser/location.cmi types/sortedMap.cmi types/types.cmi \
runtime/value.cmi runtime/load_xml.cmi
runtime/load_xml.cmx: parser/location.cmx types/sortedMap.cmx types/types.cmx \
......@@ -94,19 +98,22 @@ driver/cduce.cmx: parser/ast.cmx types/builtin.cmx runtime/eval.cmx \
parser/wlexer.cmx driver/cduce.cmi
driver/run.cmo: driver/cduce.cmi parser/location.cmi misc/state.cmi
driver/run.cmx: driver/cduce.cmx parser/location.cmx misc/state.cmx
driver/webiface.cmo: driver/cduce.cmi runtime/eval.cmi runtime/load_xml.cmi \
parser/location.cmi misc/state.cmi
driver/webiface.cmx: driver/cduce.cmx runtime/eval.cmx runtime/load_xml.cmx \
parser/location.cmx misc/state.cmx
driver/webiface.cmo: driver/cduce.cmi driver/examples.cmo parser/location.cmi \
misc/state.cmi
driver/webiface.cmx: driver/cduce.cmx driver/examples.cmx parser/location.cmx \
misc/state.cmx
toplevel/toploop.cmo: parser/parser.cmi
toplevel/toploop.cmx: parser/parser.cmx
parser/parser.cmi: parser/ast.cmo
typing/typer.cmi: parser/ast.cmo typing/typed.cmo types/types.cmi
types/boolean.cmi: types/sortedList.cmi
types/normal.cmi: types/boolean.cmi
types/patterns.cmi: types/sortedList.cmi types/sortedMap.cmi types/types.cmi
types/sequence.cmi: types/types.cmi
types/sortedMap.cmi: types/sortedList.cmi
types/syntax.cmi: types/patterns.cmi types/types.cmi
types/types.cmi: types/atoms.cmi types/chars.cmi types/intervals.cmi \
misc/pool.cmi
types/types.cmi: types/atoms.cmi types/boolean.cmi types/chars.cmi \
types/intervals.cmi misc/pool.cmi
runtime/eval.cmi: typing/typed.cmo runtime/value.cmi
runtime/load_xml.cmi: runtime/value.cmi
runtime/run_dispatch.cmi: types/patterns.cmi runtime/value.cmi
......
......@@ -6,7 +6,7 @@ let eval_env = Eval.global_env
let print_norm ppf d =
Location.protect ppf
(fun ppf -> Types.Print.print_descr ppf (Types.normalize d))
(fun ppf -> Types.Print.print_descr ppf ((*Types.normalize*) d))
let print_value ppf v =
Location.protect ppf (fun ppf -> Value.print ppf v)
......@@ -112,6 +112,8 @@ let debug ppf = function
Patterns.Compile.debug_compile ppf t pl
| `Normal_record t ->
Format.fprintf ppf "[DEBUG:normal_record]@\n";
()
(*
let t = Types.descr (Typer.typ !glb_env t) in
let count = ref 0 and seen = ref [] in
match Types.Record.first_label t with
......@@ -128,6 +130,7 @@ let debug ppf = function
Format.fprintf ppf "@] Absent: @[%a@])@\n"
Types.Print.print_descr
(match ab with Some x -> x | None -> Types.empty)
*)
(*
| `Normal_record t ->
Format.fprintf ppf "[DEBUG:normal_record]@\n";
......
......@@ -22,6 +22,7 @@ let fun f4 (A -> String; ['0'--'9'+] -> Int)
| x -> int_of x;;
f4 \"123\";;
";"mutrec","
(* All the types submitted at once are mutually recursive *)
type T = <t>S;;
......@@ -37,6 +38,7 @@ let a = 2;;
let fun h (x : Int) : Int = f x;;
(* f and g are mutually recursive, but they cannot use h *)
";"seq","
(* Sequence are just defined with pairs and the atom `nil;
the following notation are equivalent: *)
......@@ -53,6 +55,7 @@ let l6 = [ 1 2 3 ] @ [ 4 5 6 ];;
(* Inside [...], it is possible to escape a subsequence with a ! *)
let l7 = [ 1 2 !l6 !l1 5 ];;
";"seqtypes","
(* Sequence types are defined with regular expression over types *)
type IntList = [ Int* ];;
......@@ -60,8 +63,9 @@ type IntStringList = [ (Int String)* ];;
type IntNonEmptyList = [ Int+ ];;
let l : IntList = [ 1 2 3 ];;
";"integers","
(* Yes, CDuce can handle large integers ! *)
(* Yes, CDuce can handle large integers! *)
let fun facto (Int -> Int)
| 0 | 1 -> 1
| n -> n * (facto (n - 1))
......@@ -74,6 +78,7 @@ let fun facto ((Int,Int) -> Int)
| (x, n) -> facto (x * n, n - 1)
in
facto (1,10000);;
";"sumtype","
type Expr =
(`add, Expr, Expr)
......@@ -86,10 +91,11 @@ let fun eval ( Expr -> Int )
| (`add,x,y) -> eval x + eval y
| (`mul,x,y) -> eval x * eval y
| (`sub,x,y) -> eval x - eval y
| (`div,x,y) -> eval x / eval y
| (`div,x,y) -> (eval x) div (eval y)
| n -> n
in
eval (`add, 10, (`mul, 20, 5));;
";"ovfun","
type Person = FPerson | MPerson;;
type FPerson = <person gender = \"F\" >[ Name Children (Tel | Email)?];;
......@@ -131,6 +137,7 @@ let base : Person =
;;
sort base;;
";"note","
type Doc = <doc>Text;;
type Text = [ (Char | (Letter+ ' '* Note))* ];;
......@@ -157,6 +164,7 @@ let src : Doc = <doc>[ 'CDuce ' <note>\"Frisch, Castagna, Benzaken\"
'-friendly programming language.' ];;
format src;;
";"biblio","
type Biblio = <bibliography>[Heading Paper*];;
type Heading = <heading>[ PCDATA ];;
......@@ -219,43 +227,105 @@ let bib : Biblio =
];;
do_biblio bib;;
";"projection","
(* The projection e/t is translated to:
transform e with [ (x::t|_)* ] -> x *)
type Biblio = <bibliography>[Heading Paper*];;
type Heading = <heading>[ PCDATA ];;
type Paper = <paper>[ Author+ Title Conference File ];;
type Author = <author>[ PCDATA ];;
type Title = <title>[ PCDATA ];;
type Conference = <conference>[ PCDATA ];;
type File = <file>[ PCDATA ];;
let bib : Biblio =
<bibliography>[
<heading>\"Alain Frisch's bibliography\"
<paper>[
<author>\"Alain Frisch\"
<author>\"Giuseppe Castagna\"
<author>\"Vronique Benzaken\"
<title>\"Semantic subtyping\"
<conference>\"LICS 02\"
<file>\"semsub.ps.gz\"
]
<paper>[
<author>\"Mariangiola Dezani-Ciancaglini\"
<author>\"Alain Frisch\"
<author>\"Elio Giovannetti\"
<author>\"Yoko Motohama\"
<title>\"The Relevance of Semantic Subtyping\"
<conference>\"ITRS'02\"
<file>\"itrs02.ps.gz\"
]
<paper>[
<author>\"Vronique Benzaken\"
<author>\"Giuseppe Castagna\"
<author>\"Alain Frisch\"
<title>\"CDuce: a white-paper\"
<conference>\"PLANX-02\"
<file>\"planx.ps.gz\"
]
];;
let titles = [bib]/<paper>_/<title>_;;
let authors = [bib]/<paper>_/<author>_;;
let titles_concat = [bib]/<paper>_/<title>_/Char;;
"; ];;
let present = "<ul
><li
><a href=\"/cgi-bin/cduce2?example=functions\"
><a href=\"/cgi-bin/cduce?example=functions\"
>Functions.</a
>Several syntaxes to define functions.</li
> Several syntaxes to define functions.
</li
><li
><a href=\"/cgi-bin/cduce2?example=mutrec\"
><a href=\"/cgi-bin/cduce?example=mutrec\"
>Mutual recursion.</a
>Mutual toplevel definition for types and functions.</li
> Mutual toplevel definition for types and functions.
</li
><li
><a href=\"/cgi-bin/cduce2?example=seq\"
><a href=\"/cgi-bin/cduce?example=seq\"
>Sequence literals.</a
>How to write sequences.</li
> How to write sequences.
</li
><li
><a href=\"/cgi-bin/cduce2?example=seqtypes\"
><a href=\"/cgi-bin/cduce?example=seqtypes\"
>Sequence types.</a
>Types for sequences.</li
> Types for sequences.
</li
><li
><a href=\"/cgi-bin/cduce2?example=integers\"
><a href=\"/cgi-bin/cduce?example=integers\"
>The factorial function.</a
>What about computing 10000! ?</li
> What about computing 10000! ?
</li
><li
><a href=\"/cgi-bin/cduce2?example=sumtype\"
><a href=\"/cgi-bin/cduce?example=sumtype\"
>Sum types.</a
>How to simulate ML sum types.</li
> How to simulate ML sum types.
</li
><li
><a href=\"/cgi-bin/cduce2?example=ovfun\"
><a href=\"/cgi-bin/cduce?example=ovfun\"
>Overloaded functions.</a
>This examples demonstrates the use of overloaded functions.</li
> This examples demonstrates the use of overloaded functions.
</li
><li
><a href=\"/cgi-bin/cduce2?example=note\"
><a href=\"/cgi-bin/cduce?example=note\"
>Footnotes.</a
> This example shows how to bind an XML element with surrounding text.</li
> This example shows how to bind an XML element with surrounding text.
</li
><li
><a href=\"/cgi-bin/cduce2?example=biblio\"
><a href=\"/cgi-bin/cduce?example=biblio\"
>Bibliography.</a
>The good old XML bibliography example.</li
> The good old XML bibliography example.
</li
><li
><a href=\"/cgi-bin/cduce?example=projection\"
>Projection.</a
> Syntactic sugar for projection.
</li
></ul
>";;
\ No newline at end of file
......@@ -288,7 +288,7 @@ let main (cgi : Netcgi.std_activation) =
fatal_error "Internal software error!" msg
let () =
Unix.alarm 20;
ignore (Unix.alarm 20);
Sys.set_signal Sys.sigalrm (Sys.Signal_handle (fun _ -> raise Timeout));
main cgi;
cgi # finalize ()
......
......@@ -92,6 +92,12 @@ let nb_classes = 34
let c = int_of_string s in
assert ( c < 256 ); (* TODO: handle Unicode *)
Char.chr c
let rec tag_of_tag s i =
match s.[i] with
| '\008' | '\009' | '\010' | '\013' | '\032' -> tag_of_tag s (i+1)
| _ -> String.sub s i (String.length s - i)
let lex_tables = {
Lexing.lex_base =
"\000\000\009\000\012\000\018\000\252\255\251\255\004\000\255\255\
......@@ -157,31 +163,31 @@ let lex_tables = {
let rec token engine lexbuf =
match engine lex_tables 0 lexbuf with
0 -> (
# 44 "parser/wlexer.mll"
# 50 "parser/wlexer.mll"
token engine lexbuf )
| 1 -> (
# 45 "parser/wlexer.mll"
# 51 "parser/wlexer.mll"
let s = Lexing.lexeme lexbuf in
if Hashtbl.mem keywords s then "",s else "LIDENT",s
)
| 2 -> (
# 49 "parser/wlexer.mll"
# 55 "parser/wlexer.mll"
"UIDENT",Lexing.lexeme lexbuf )
| 3 -> (
# 50 "parser/wlexer.mll"
# 56 "parser/wlexer.mll"
"INT",Lexing.lexeme lexbuf )
| 4 -> (
# 51 "parser/wlexer.mll"
# 57 "parser/wlexer.mll"
let s = Lexing.lexeme lexbuf in
"TAG", String.sub s 1 (String.length s - 1)
"TAG", tag_of_tag s 1
)
| 5 -> (
# 58 "parser/wlexer.mll"
# 64 "parser/wlexer.mll"
"",Lexing.lexeme lexbuf )
| 6 -> (
# 61 "parser/wlexer.mll"
# 67 "parser/wlexer.mll"
let string_start = Lexing.lexeme_start lexbuf in
string_start_pos := string_start;
let double_quote = Lexing.lexeme_char lexbuf 0 = '"' in
......@@ -191,15 +197,15 @@ let rec token engine lexbuf =
(if double_quote then "STRING2" else "STRING1"),
(get_stored_string()) )
| 7 -> (
# 71 "parser/wlexer.mll"
# 77 "parser/wlexer.mll"
comment_start_pos := [Lexing.lexeme_start lexbuf];
comment engine lexbuf;
token engine lexbuf )
| 8 -> (
# 76 "parser/wlexer.mll"
# 82 "parser/wlexer.mll"
"EOI","" )
| 9 -> (
# 78 "parser/wlexer.mll"
# 84 "parser/wlexer.mll"
error
(Lexing.lexeme_start lexbuf) (Lexing.lexeme_end lexbuf)
(Illegal_character ((Lexing.lexeme lexbuf).[0])) )
......@@ -208,17 +214,17 @@ let rec token engine lexbuf =
and comment engine lexbuf =
match engine lex_tables 1 lexbuf with
0 -> (
# 84 "parser/wlexer.mll"
# 90 "parser/wlexer.mll"
comment_start_pos := Lexing.lexeme_start lexbuf :: !comment_start_pos;
comment engine lexbuf;
)
| 1 -> (
# 88 "parser/wlexer.mll"
# 94 "parser/wlexer.mll"
comment_start_pos := List.tl !comment_start_pos;
if !comment_start_pos <> [] then comment engine lexbuf;
)
| 2 -> (
# 92 "parser/wlexer.mll"
# 98 "parser/wlexer.mll"
string_start_pos := Lexing.lexeme_start lexbuf;
let string =
if Lexing.lexeme_char lexbuf 0 = '"' then string2 else string1 in
......@@ -229,33 +235,33 @@ and comment engine lexbuf =
Buffer.clear string_buff;
comment engine lexbuf )
| 3 -> (
# 102 "parser/wlexer.mll"
# 108 "parser/wlexer.mll"
let st = List.hd !comment_start_pos in
error st (st+2) Unterminated_comment
)
| 4 -> (
# 106 "parser/wlexer.mll"
# 112 "parser/wlexer.mll"
comment engine lexbuf )
| _ -> failwith "lexing: empty token [comment]"
and string2 engine lexbuf =
match engine lex_tables 2 lexbuf with
0 -> (
# 110 "parser/wlexer.mll"
# 116 "parser/wlexer.mll"
() )
| 1 -> (
# 112 "parser/wlexer.mll"
# 118 "parser/wlexer.mll"
store_char (Lexing.lexeme_char lexbuf 1);
string2 engine lexbuf )
| 2 -> (
# 115 "parser/wlexer.mll"
# 121 "parser/wlexer.mll"
store_char (char_for_decimal_code (Lexing.lexeme lexbuf));
string2 engine lexbuf )
| 3 -> (
# 118 "parser/wlexer.mll"
# 124 "parser/wlexer.mll"
error !string_start_pos (!string_start_pos+1) Unterminated_string )
| 4 -> (
# 120 "parser/wlexer.mll"
# 126 "parser/wlexer.mll"
store_char (Lexing.lexeme_char lexbuf 0);
(* TODO: Unicode *)
string2 engine lexbuf )
......@@ -264,28 +270,28 @@ and string2 engine lexbuf =
and string1 engine lexbuf =
match engine lex_tables 3 lexbuf with
0 -> (
# 126 "parser/wlexer.mll"
# 132 "parser/wlexer.mll"
() )
| 1 -> (
# 128 "parser/wlexer.mll"
# 134 "parser/wlexer.mll"
store_char (Lexing.lexeme_char lexbuf 1);
string1 engine lexbuf )
| 2 -> (
# 131 "parser/wlexer.mll"
# 137 "parser/wlexer.mll"
store_char (char_for_decimal_code (Lexing.lexeme lexbuf));
string1 engine lexbuf )
| 3 -> (
# 134 "parser/wlexer.mll"
# 140 "parser/wlexer.mll"
error !string_start_pos (!string_start_pos+1) Unterminated_string )
| 4 -> (
# 136 "parser/wlexer.mll"
# 142 "parser/wlexer.mll"
store_char (Lexing.lexeme_char lexbuf 0);
string1 engine lexbuf )
| _ -> failwith "lexing: empty token [string1]"
;;
# 139 "parser/wlexer.mll"
# 145 "parser/wlexer.mll"
let lexer_func_of_wlex lexfun lexengine cs =
......
......@@ -93,4 +93,6 @@ do_biblio bib
;;
(*
[bib]/<paper>_/<author>_;;
*)
......@@ -85,8 +85,19 @@ let rec map f t =
in
SortedList.from_list lines
let iter f t =
List.iter (fun (p,n) -> List.iter f p; List.iter f n) t
let compute ~empty ~full ~cup ~cap ~diff ~atom t =
let line (p,n) =
List.fold_left (fun accu x -> diff accu (atom x)) (
List.fold_left (fun accu x -> cap accu (atom x)) full p
) n in
List.fold_left (fun accu l -> cup accu (line l)) empty t
let compute_bool f =
compute ~empty ~full ~cup ~cap ~diff ~atom:f
let equal_list e l1 l2 =
if List.length l1 <> List.length l2 then raise Recursive.NotEqual;
List.iter2 e l1 l2
......
......@@ -22,7 +22,11 @@ val diff : 'a t -> 'a t -> 'a t
val atom : 'a -> 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
val iter: ('a -> unit) -> 'a t -> unit
val compute: empty:'d -> full:'c -> cup:('d -> 'c -> 'd)
-> cap:('c -> 'b -> 'c) -> diff:('c -> 'b -> 'c) ->
atom:('a -> 'b) -> 'a t -> 'd
val compute_bool: ('a -> 'b t) -> 'a t -> 'b t
val equal: ('a -> 'b -> unit) -> 'a t -> 'b t -> unit
......
module type S =
sig
type t
val any: t
val cup: t -> t -> t
val cap: t -> t -> t
val diff: t -> t -> t
val empty: t -> bool
end
module Make(X1 : S)(X2 : S) =
struct
type t = (X1.t * X2.t) list
type cell = { mutable t1 : X1.t; mutable t2 : X2.t; next: cell }
(* Quite ugly, isn't it ?
I _want_ sum+records types in OCaml ! *)
(* Possible optimizations:
- check whether t1 or t2 is empty initially
- check s1 = t1 (structural equility)
*)
let rec add root t1 t2 l =
if (Obj.magic l = 0) then root := { t1 = t1; t2 = t2; next = !root }
else
match l with
{ t1 = s1; t2 = s2; next = next } ->
let i = X1.cap t1 s1 in
if X1.empty i then add root t1 t2 l.next
else (
l.t1 <- i; l.t2 <- X2.cup t2 s2;
let k = X1.diff s1 t1 in
if not (X1.empty k) then root := { t1 = k; t2 = s2; next = !root };
let j = X1.diff t1 s1 in
if not (X1.empty j) then add root j t2 next
)
let rec get accu l =
if Obj.magic l = 0 then accu
else get ((l.t1, l.t2)::accu) l.next
let normal x =
let res = ref (Obj.magic 0) in
List.iter (fun (t1,t2) -> add res t1 t2 !res) x;
get [] !res
let rec bigcap_aux t1 t2 = function
| (s1,s2)::rem -> bigcap_aux (X1.cap t1 s1) (X2.cap t2 s2) rem
| [] -> (t1,t2)
let bigcap = bigcap_aux X1.any X2.any
let boolean x =
let res = ref (Obj.magic 0) in
let line (p,n) =
let (d1,d2) = bigcap p in
if not ((X1.empty d1) || (X2.empty d2)) then
(let resid = ref d1 in
List.iter
(fun (t1,t2) ->
let t1 = X1.cap d1 t1 in
if not (X1.empty t1) then
(resid := X1.diff !resid t1;
let t2 = X2.diff d2 t2 in
if not (X2.empty t2) then add res t1 t2 !res
)
) (normal n);
if not (X1.empty !resid) then add res !resid d2 !res)
in
List.iter line x;
get [] !res
end
module type S =
sig
type t
val any: t
val cup: t -> t -> t
val cap: t -> t -> t
val diff: t -> t -> t
val empty: t -> bool
end
module Make(X1 : S)(X2 : S) :
sig