2390 lines
70 KiB
OCaml
2390 lines
70 KiB
OCaml
(***********************************************************************)
|
|
(* *)
|
|
(* Objective Caml *)
|
|
(* *)
|
|
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
|
|
(* *)
|
|
(* Copyright 1996 Institut National de Recherche en Informatique et *)
|
|
(* en Automatique. All rights reserved. This file is distributed *)
|
|
(* under the terms of the Q Public License version 1.0. *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
|
|
(* $Id$ *)
|
|
|
|
(* Compilation of pattern matching *)
|
|
|
|
open Misc
|
|
open Asttypes
|
|
open Primitive
|
|
open Types
|
|
open Typedtree
|
|
open Lambda
|
|
open Parmatch
|
|
|
|
(* See Peyton-Jones, ``The Implementation of functional programming
|
|
languages'', chapter 5. *)
|
|
(*
|
|
Bon, au commencement du monde c'etait vrai.
|
|
*)
|
|
|
|
type matrix = pattern list list
|
|
|
|
type ctx = {left:pattern list ; right:pattern list}
|
|
|
|
let pretty_ctx ctx =
|
|
List.iter
|
|
(fun {left=left ; right=right} ->
|
|
prerr_string "LEFT:" ;
|
|
pretty_line left ;
|
|
prerr_string " RIGHT:" ;
|
|
pretty_line right ;
|
|
prerr_endline "")
|
|
ctx
|
|
|
|
let le_ctx c1 c2 =
|
|
le_pats c1.left c2.left &&
|
|
le_pats c1.right c2.right
|
|
|
|
let lshift {left=left ; right=right} = match right with
|
|
| x::xs -> {left=x::left ; right=xs}
|
|
| _ -> assert false
|
|
|
|
let lforget {left=left ; right=right} = match right with
|
|
| x::xs -> {left=omega::left ; right=xs}
|
|
| _ -> assert false
|
|
|
|
let rec small_enough n = function
|
|
| [] -> true
|
|
| _::rem ->
|
|
if n <= 0 then false
|
|
else small_enough (n-1) rem
|
|
|
|
let ctx_lshift ctx =
|
|
if small_enough 31 ctx then
|
|
List.map lshift ctx
|
|
else (* Context pruning *) begin
|
|
get_mins le_ctx (List.map lforget ctx)
|
|
end
|
|
|
|
let rshift {left=left ; right=right} = match left with
|
|
| p::ps -> {left=ps ; right=p::right}
|
|
| _ -> assert false
|
|
|
|
let ctx_rshift ctx = List.map rshift ctx
|
|
|
|
let rec nchars n ps =
|
|
if n <= 0 then [],ps
|
|
else match ps with
|
|
| p::rem ->
|
|
let chars, cdrs = nchars (n-1) rem in
|
|
p::chars,cdrs
|
|
| _ -> assert false
|
|
|
|
let rshift_num n {left=left ; right=right} =
|
|
let shifted,left = nchars n left in
|
|
{left=left ; right = shifted@right}
|
|
|
|
let ctx_rshift_num n ctx = List.map (rshift_num n) ctx
|
|
|
|
let combine {left=left ; right=right} = match left with
|
|
| p::ps -> {left=ps ; right=set_args p right}
|
|
| _ -> assert false
|
|
|
|
let ctx_combine ctx = List.map combine ctx
|
|
|
|
let ncols = function
|
|
| [] -> 0
|
|
| ps::_ -> List.length ps
|
|
|
|
|
|
exception NoMatch
|
|
exception OrPat
|
|
exception Unused
|
|
|
|
let filter_matrix matcher pss =
|
|
|
|
let rec filter_rec = function
|
|
| (p::ps)::rem ->
|
|
begin match p.pat_desc with
|
|
| Tpat_alias (p,_) ->
|
|
filter_rec ((p::ps)::rem)
|
|
| Tpat_var _ ->
|
|
filter_rec ((omega::ps)::rem)
|
|
| _ ->
|
|
begin
|
|
let rem = filter_rec rem in
|
|
try
|
|
matcher p ps::rem
|
|
with
|
|
| NoMatch -> rem
|
|
| OrPat ->
|
|
match p.pat_desc with
|
|
| Tpat_or (p1,p2,_) -> filter_rec [(p1::ps) ;(p2::ps)]@rem
|
|
| _ -> assert false
|
|
end
|
|
end
|
|
| [] -> []
|
|
| _ ->
|
|
pretty_matrix pss ;
|
|
fatal_error "Matching.filter_matrix" in
|
|
filter_rec pss
|
|
|
|
let ctx_matcher p =
|
|
let p = normalize_pat p in
|
|
match p.pat_desc with
|
|
| Tpat_construct (cstr,omegas) ->
|
|
(fun q rem -> match q.pat_desc with
|
|
| Tpat_construct (cstr',args) when cstr.cstr_tag=cstr'.cstr_tag ->
|
|
p,args @ rem
|
|
| Tpat_any -> p,omegas @ rem
|
|
| _ -> raise NoMatch)
|
|
| Tpat_constant cst ->
|
|
(fun q rem -> match q.pat_desc with
|
|
| Tpat_constant cst' when cst=cst' ->
|
|
p,rem
|
|
| Tpat_any -> p,rem
|
|
| _ -> raise NoMatch)
|
|
| Tpat_variant (lab,Some omega,_) ->
|
|
(fun q rem -> match q.pat_desc with
|
|
| Tpat_variant (lab',Some arg,_) when lab=lab' ->
|
|
p,arg::rem
|
|
| Tpat_any -> p,omega::rem
|
|
| _ -> raise NoMatch)
|
|
| Tpat_variant (lab,None,_) ->
|
|
(fun q rem -> match q.pat_desc with
|
|
| Tpat_variant (lab',None,_) when lab=lab' ->
|
|
p,rem
|
|
| Tpat_any -> p,rem
|
|
| _ -> raise NoMatch)
|
|
| Tpat_array omegas ->
|
|
let len = List.length omegas in
|
|
(fun q rem -> match q.pat_desc with
|
|
| Tpat_array args when List.length args=len ->
|
|
p,args @ rem
|
|
| Tpat_any -> p, omegas @ rem
|
|
| _ -> raise NoMatch)
|
|
| Tpat_tuple omegas ->
|
|
(fun q rem -> match q.pat_desc with
|
|
| Tpat_tuple args -> p,args @ rem
|
|
| _ -> p, omegas @ rem)
|
|
| Tpat_record l -> (* Records are normalized *)
|
|
(fun q rem -> match q.pat_desc with
|
|
| Tpat_record l' ->
|
|
let l' = all_record_args l' in
|
|
p, List.fold_right (fun (_,p) r -> p::r) l' rem
|
|
| _ -> p,List.fold_right (fun (_,p) r -> p::r) l rem)
|
|
| _ -> fatal_error "Matching.ctx_matcher"
|
|
|
|
|
|
|
|
|
|
let filter_ctx q ctx =
|
|
|
|
let matcher = ctx_matcher q in
|
|
|
|
let rec filter_rec = function
|
|
| ({right=p::ps} as l)::rem ->
|
|
begin match p.pat_desc with
|
|
| Tpat_or (p1,p2,_) ->
|
|
filter_rec ({l with right=p1::ps}::{l with right=p2::ps}::rem)
|
|
| Tpat_alias (p,_) ->
|
|
filter_rec ({l with right=p::ps}::rem)
|
|
| Tpat_var _ ->
|
|
filter_rec ({l with right=omega::ps}::rem)
|
|
| _ ->
|
|
begin let rem = filter_rec rem in
|
|
try
|
|
let to_left, right = matcher p ps in
|
|
{left=to_left::l.left ; right=right}::rem
|
|
with
|
|
| NoMatch -> rem
|
|
end
|
|
end
|
|
| [] -> []
|
|
| _ -> fatal_error "Matching.filter_ctx" in
|
|
|
|
filter_rec ctx
|
|
|
|
let select_columns pss ctx =
|
|
let n = ncols pss in
|
|
List.fold_right
|
|
(fun ps r ->
|
|
List.fold_right
|
|
(fun {left=left ; right=right} r ->
|
|
let transfert, right = nchars n right in
|
|
try
|
|
{left = lubs transfert ps @ left ; right=right}::r
|
|
with
|
|
| Empty -> r)
|
|
ctx r)
|
|
pss []
|
|
|
|
let ctx_lub p ctx =
|
|
List.fold_right
|
|
(fun {left=left ; right=right} r ->
|
|
match right with
|
|
| q::rem ->
|
|
begin try
|
|
{left=left ; right = lub p q::rem}::r
|
|
with
|
|
| Empty -> r
|
|
end
|
|
| _ -> fatal_error "Matching.ctx_lub")
|
|
ctx []
|
|
|
|
let ctx_match ctx pss =
|
|
List.exists
|
|
(fun {right=qs} ->
|
|
List.exists
|
|
(fun ps -> compats qs ps)
|
|
pss)
|
|
ctx
|
|
|
|
type jumps = (int * ctx ) list
|
|
|
|
let pretty_jumps env = match env with
|
|
| [] -> ()
|
|
| _ ->
|
|
List.iter
|
|
(fun (i,ctx) ->
|
|
Printf.fprintf stderr "jump for %d\n" i ;
|
|
pretty_ctx ctx)
|
|
env
|
|
|
|
|
|
let rec jumps_extract i = function
|
|
| [] -> [],[]
|
|
| (j,pss) as x::rem as all ->
|
|
if i=j then pss,rem
|
|
else if j < i then [],all
|
|
else
|
|
let r,rem = jumps_extract i rem in
|
|
r,(x::rem)
|
|
|
|
let rec jumps_remove i = function
|
|
| [] -> []
|
|
| (j,_)::rem when i=j -> rem
|
|
| x::rem -> x::jumps_remove i rem
|
|
|
|
let jumps_empty = []
|
|
and jumps_is_empty = function
|
|
| [] -> true
|
|
| _ -> false
|
|
|
|
let jumps_singleton i = function
|
|
| [] -> []
|
|
| ctx -> [i,ctx]
|
|
|
|
let jumps_add i pss jumps = match pss with
|
|
| [] -> jumps
|
|
| _ ->
|
|
let rec add = function
|
|
| [] -> [i,pss]
|
|
| (j,qss) as x::rem as all ->
|
|
if j > i then x::add rem
|
|
else if j < i then (i,pss)::all
|
|
else (i,(get_mins le_ctx (pss@qss)))::rem in
|
|
add jumps
|
|
|
|
|
|
let rec jumps_union env1 env2 = match env1,env2 with
|
|
| [],_ -> env2
|
|
| _,[] -> env1
|
|
| ((i1,pss1) as x1::rem1), ((i2,pss2) as x2::rem2) ->
|
|
if i1=i2 then
|
|
(i1,get_mins le_ctx (pss1@pss2))::jumps_union rem1 rem2
|
|
else if i1 > i2 then
|
|
x1::jumps_union rem1 env2
|
|
else
|
|
x2::jumps_union env1 rem2
|
|
|
|
|
|
let rec merge = function
|
|
| env1::env2::rem -> jumps_union env1 env2::merge rem
|
|
| envs -> envs
|
|
|
|
let rec jumps_unions envs = match envs with
|
|
| [] -> []
|
|
| [env] -> env
|
|
| _ -> jumps_unions (merge envs)
|
|
|
|
let rec jumps_map f env =
|
|
List.map
|
|
(fun (i,pss) -> i,f pss)
|
|
env
|
|
|
|
type pattern_matching =
|
|
{ mutable cases : (pattern list * lambda) list;
|
|
args : (lambda * let_kind) list ;
|
|
default : int * (matrix * int) list}
|
|
|
|
type pattern_matching_ext =
|
|
{to_match : pattern_matching ;
|
|
to_catch : (matrix * int * Ident.t list * pattern_matching) list}
|
|
|
|
|
|
|
|
let pretty_cases cases =
|
|
List.iter
|
|
(fun ((ps),l) ->
|
|
List.iter
|
|
(fun p ->
|
|
Parmatch.top_pretty Format.str_formatter p ;
|
|
prerr_string " " ;
|
|
prerr_string (Format.flush_str_formatter ()))
|
|
ps ;
|
|
prerr_string " -> " ;
|
|
Printlambda.lambda Format.str_formatter l ;
|
|
prerr_string (Format.flush_str_formatter ()) ;
|
|
prerr_endline "")
|
|
cases
|
|
|
|
let pretty_pm pm = pretty_cases pm.cases
|
|
|
|
let pretty_def def =
|
|
List.iter
|
|
(fun (pss,i) ->
|
|
Printf.fprintf stderr "Matrix for %d\n" i ;
|
|
pretty_matrix pss)
|
|
def
|
|
|
|
let pretty_ext m =
|
|
prerr_endline "++++++++ To Match ++++++++" ;
|
|
pretty_pm m.to_match ;
|
|
begin match m.to_catch with
|
|
| [] ->
|
|
prerr_endline "++++++++ No Catch ++++++++"
|
|
| to_catch ->
|
|
prerr_endline "++++++++ To Catch ++++++++" ;
|
|
List.iter
|
|
(fun (p,i,_,pm) ->
|
|
Printf.fprintf stderr "Handler %d: " i ;
|
|
prerr_endline "" ;
|
|
pretty_pm pm)
|
|
to_catch
|
|
end ;
|
|
prerr_endline "+++++ Defaults +++++" ;
|
|
pretty_def (snd m.to_match.default) ;
|
|
prerr_endline "+++++++++++++++++++++"
|
|
|
|
|
|
(* To group lines of patterns with identical keys *)
|
|
|
|
let add_line patl_action pm =
|
|
pm.cases <- patl_action :: pm.cases; pm
|
|
|
|
type cell =
|
|
{pm : pattern_matching ;
|
|
ctx : ctx list ;
|
|
pat : pattern}
|
|
|
|
let add make_matching_fun division key patl_action args =
|
|
try
|
|
let cell = List.assoc key division in
|
|
cell.pm.cases <- patl_action :: cell.pm.cases;
|
|
division
|
|
with Not_found ->
|
|
let cell = make_matching_fun args in
|
|
cell.pm.cases <- [patl_action] ;
|
|
(key, cell) :: division
|
|
|
|
|
|
|
|
(* To find reasonable names for let-bound and lambda-bound idents *)
|
|
|
|
let rec name_pattern default = function
|
|
(pat :: patl, action) :: rem ->
|
|
begin match pat.pat_desc with
|
|
Tpat_var id -> id
|
|
| Tpat_alias(p, id) -> id
|
|
| _ -> name_pattern default rem
|
|
end
|
|
| _ -> Ident.create default
|
|
|
|
|
|
exception Not_simple
|
|
|
|
let rec raw_rec env = function
|
|
| Llet(Alias,x,ex, body) -> raw_rec ((x,raw_rec env ex)::env) body
|
|
| Lvar id as l ->
|
|
begin try List.assoc id env with
|
|
| Not_found -> l
|
|
end
|
|
| Lprim (Pfield i,args) ->
|
|
Lprim (Pfield i, List.map (raw_rec env) args)
|
|
| Lconst _ as l -> l
|
|
| Lstaticraise (i,args) ->
|
|
Lstaticraise (i, List.map (raw_rec env) args)
|
|
| _ -> raise Not_simple
|
|
|
|
let raw_action l = try raw_rec [] l with Not_simple -> l
|
|
|
|
let same_actions = function
|
|
| [] -> None
|
|
| [_,act] -> Some act
|
|
| (_,act0) :: rem ->
|
|
try
|
|
let raw_act0 = raw_rec [] act0 in
|
|
let rec s_rec = function
|
|
| [] -> Some act0
|
|
| (_,act)::rem ->
|
|
if raw_act0 = raw_rec [] act then
|
|
s_rec rem
|
|
else
|
|
None in
|
|
s_rec rem
|
|
with
|
|
| Not_simple -> None
|
|
|
|
let equal_action act1 act2 =
|
|
try
|
|
let raw1 = raw_rec [] act1
|
|
and raw2 = raw_rec [] act2 in
|
|
raw1 = raw2
|
|
with
|
|
| Not_simple -> false
|
|
|
|
let up_ok_action act1 act2 =
|
|
try
|
|
let raw1 = raw_rec [] act1
|
|
and raw2 = raw_rec [] act2 in
|
|
match raw1, raw2 with
|
|
| Lstaticraise (i1,_), Lstaticraise (i2,_) -> i1=i2
|
|
| _,_ -> raw1 = raw2
|
|
with
|
|
| Not_simple -> false
|
|
|
|
let up_ok (ps,act_p) l =
|
|
List.for_all
|
|
(fun (qs,act_q) ->
|
|
up_ok_action act_p act_q ||
|
|
not (Parmatch.compats ps qs))
|
|
l
|
|
|
|
exception Same
|
|
|
|
let rec what_is_or = function
|
|
| {pat_desc = Tpat_or (p,_,_)} -> what_is_or p
|
|
| {pat_desc = (Tpat_alias (p,_))} -> what_is_or p
|
|
| {pat_desc=(Tpat_var _|Tpat_any)} -> fatal_error "Matching.what_is_or"
|
|
| p -> p
|
|
|
|
|
|
(*
|
|
Simplify fonction normalize the first column of the match
|
|
- records are expanded so that they posses all fields
|
|
- or-patterns equivalent to variables are replaced by those variables
|
|
*)
|
|
|
|
exception Var of pattern
|
|
|
|
|
|
let simplify_or p =
|
|
let rec simpl_rec p = match p with
|
|
| {pat_desc = Tpat_any|Tpat_var _} -> raise (Var p)
|
|
| {pat_desc = Tpat_alias (q,id)} ->
|
|
begin try
|
|
{p with pat_desc = Tpat_alias (simpl_rec q,id)}
|
|
with
|
|
| Var q -> raise (Var {p with pat_desc = Tpat_alias (q,id)})
|
|
end
|
|
| {pat_desc = Tpat_or (p1,p2,_)} ->
|
|
{p with pat_desc = Tpat_or (simpl_rec p1, simpl_rec p2, None)}
|
|
| {pat_desc = Tpat_record lbls} ->
|
|
let all_lbls = all_record_args lbls in
|
|
{p with pat_desc=Tpat_record all_lbls}
|
|
| _ -> p in
|
|
try
|
|
simpl_rec p
|
|
with
|
|
| Var p -> p
|
|
|
|
|
|
let simplify_matching m = match m.args with
|
|
| [] -> omega,m
|
|
| (arg, _) :: _ ->
|
|
let ex_pat = ref None in
|
|
let record_ex_pat p = match !ex_pat with
|
|
| None -> ex_pat := Some p
|
|
| _ -> () in
|
|
|
|
let rec simplify = function
|
|
| (pat :: patl, action as patl_action) :: rem ->
|
|
begin match pat.pat_desc with
|
|
| Tpat_var id ->
|
|
(omega :: patl, bind Alias id arg action) ::
|
|
simplify rem
|
|
| Tpat_any ->
|
|
patl_action :: simplify rem
|
|
| Tpat_alias(p, id) ->
|
|
simplify ((p :: patl, bind Alias id arg action) :: rem)
|
|
| Tpat_record [] ->
|
|
(omega :: patl, action)::
|
|
simplify rem
|
|
| Tpat_record lbls ->
|
|
let all_lbls = all_record_args lbls in
|
|
let full_pat = {pat with pat_desc=Tpat_record all_lbls} in
|
|
record_ex_pat full_pat ;
|
|
(full_pat::patl,action)::
|
|
simplify rem
|
|
| Tpat_or _ ->
|
|
let pat_simple = simplify_or pat in
|
|
begin match pat_simple.pat_desc with
|
|
| Tpat_or _ ->
|
|
let ex_pat = what_is_or pat_simple in
|
|
record_ex_pat ex_pat ;
|
|
(pat_simple :: patl, action) ::
|
|
simplify rem
|
|
| _ ->
|
|
simplify ((pat_simple::patl,action) :: rem)
|
|
end
|
|
| _ ->
|
|
record_ex_pat pat ;
|
|
patl_action :: simplify rem
|
|
end
|
|
| [] -> []
|
|
| _ -> assert false in
|
|
let cases = simplify m.cases in
|
|
match !ex_pat with
|
|
| None -> omega, {m with cases=cases}
|
|
| Some p -> p,{m with cases = cases}
|
|
|
|
|
|
|
|
let rec what_is_cases cases = match cases with
|
|
| ({pat_desc=Tpat_any} :: _, _) :: rem -> what_is_cases rem
|
|
| (p::_,_)::_ -> p
|
|
| _ -> omega
|
|
|
|
|
|
|
|
(* Optimize breaks *)
|
|
let as_matrix cases =
|
|
get_mins le_pats (List.map (fun (ps,_) -> ps) cases)
|
|
|
|
|
|
let cons_default matrix raise_num (_,default) =
|
|
match matrix with
|
|
| [] -> raise_num,default
|
|
| _ -> raise_num,((matrix,raise_num)::default)
|
|
|
|
let default_compat p (exit,def) =
|
|
exit,
|
|
List.fold_right
|
|
(fun (pss,i) r ->
|
|
let qss =
|
|
List.fold_right
|
|
(fun qs r -> match qs with
|
|
| q::rem when Parmatch.compat p q -> rem::r
|
|
| _ -> r)
|
|
pss [] in
|
|
match qss with
|
|
| [] -> r
|
|
| _ -> (qss,i)::r)
|
|
def []
|
|
|
|
let rec extract_vars r p = match p.pat_desc with
|
|
| Tpat_var id -> IdentSet.add id r
|
|
| Tpat_alias (p, id) ->
|
|
extract_vars (IdentSet.add id r) p
|
|
| Tpat_tuple pats ->
|
|
List.fold_left extract_vars r pats
|
|
| Tpat_record lpats ->
|
|
List.fold_left
|
|
(fun r (_,p) -> extract_vars r p)
|
|
r lpats
|
|
| Tpat_construct (_,pats) ->
|
|
List.fold_left extract_vars r pats
|
|
| Tpat_array pats ->
|
|
List.fold_left extract_vars r pats
|
|
| Tpat_variant (_,Some p, _) -> extract_vars r p
|
|
| Tpat_or (p,_,_) -> extract_vars r p
|
|
| Tpat_constant _|Tpat_any|Tpat_variant (_,None,_) -> r
|
|
|
|
exception Cannot_flatten
|
|
|
|
let mk_alpha_env arg aliases ids =
|
|
List.map
|
|
(fun id -> id,
|
|
if List.mem id aliases then
|
|
match arg with
|
|
| Some v -> v
|
|
| _ -> raise Cannot_flatten
|
|
else
|
|
Ident.create (Ident.name id))
|
|
ids
|
|
|
|
let rec explode_or_pat arg patl mk_action rem vars aliases = function
|
|
| {pat_desc = Tpat_or (p1,p2,_)} ->
|
|
explode_or_pat
|
|
arg patl mk_action
|
|
(explode_or_pat arg patl mk_action rem vars aliases p2)
|
|
vars aliases p1
|
|
| {pat_desc = Tpat_alias (p,id)} ->
|
|
explode_or_pat arg patl mk_action rem vars (id::aliases) p
|
|
| p ->
|
|
let env = mk_alpha_env arg aliases vars in
|
|
(alpha_pat env p::patl,mk_action (List.map snd env))::rem
|
|
|
|
let equiv_pat p q = le_pat p q && le_pat q p
|
|
|
|
let rec get_equiv p l = match l with
|
|
| (q::_,_) as cl::rem ->
|
|
if equiv_pat p q then
|
|
let others,rem = get_equiv p rem in
|
|
cl::others,rem
|
|
else
|
|
[],l
|
|
| _ -> [],l
|
|
|
|
let pm_free_variables {cases=cases} =
|
|
List.fold_right
|
|
(fun (_,act) r -> IdentSet.union (free_variables act) r)
|
|
cases IdentSet.empty
|
|
|
|
let compile_or argo cl clor al def = match clor with
|
|
| [] ->
|
|
{to_match = {cases=cl ; args=al ; default=def} ;
|
|
to_catch = []}
|
|
| _ ->
|
|
let rec do_cases = function
|
|
| ({pat_desc=Tpat_or _} as orp::patl, action)::rem ->
|
|
let others,rem = get_equiv orp rem in
|
|
let orpm =
|
|
{cases =
|
|
(patl, action)::
|
|
List.map
|
|
(function
|
|
| (_::ps,action) -> ps,action
|
|
| _ -> assert false)
|
|
others ;
|
|
args = List.tl al ;
|
|
default = default_compat orp def} in
|
|
begin match patl,action with
|
|
| [],Lstaticraise (_,[]) ->
|
|
let new_ord,new_to_catch = do_cases rem in
|
|
let mk_new_action _ = action in
|
|
explode_or_pat
|
|
argo [] mk_new_action new_ord [] [] orp,
|
|
new_to_catch
|
|
| _,_ ->
|
|
let vars =
|
|
IdentSet.elements
|
|
(IdentSet.inter
|
|
(extract_vars IdentSet.empty orp)
|
|
(pm_free_variables orpm)) in
|
|
let or_num = next_raise_count () in
|
|
let new_patl = Parmatch.omega_list patl in
|
|
|
|
let mk_new_action vs =
|
|
Lstaticraise
|
|
(or_num, List.map (fun v -> Lvar v) vs) in
|
|
|
|
let new_ord,new_to_catch = do_cases rem in
|
|
explode_or_pat
|
|
argo new_patl mk_new_action new_ord vars [] orp,
|
|
(([[orp]], or_num, vars , orpm):: new_to_catch)
|
|
end
|
|
| cl::rem ->
|
|
let new_ord,new_to_catch = do_cases rem in
|
|
cl::new_ord,new_to_catch
|
|
| [] -> [],[] in
|
|
|
|
let to_match,to_catch = do_cases clor in
|
|
{to_match = {args=al ; cases=cl@to_match ; default=def} ;
|
|
to_catch = to_catch}
|
|
|
|
|
|
(* Basic grouping predicates *)
|
|
|
|
let group_constant = function
|
|
| {pat_desc= Tpat_constant _} -> true
|
|
| _ -> false
|
|
|
|
and group_constructor = function
|
|
| {pat_desc = Tpat_construct (_, _)} -> true
|
|
| _ -> false
|
|
|
|
and group_variant = function
|
|
| {pat_desc = Tpat_variant (_, _, _)} -> true
|
|
| _ -> false
|
|
|
|
and group_var = function
|
|
| {pat_desc=Tpat_any} -> true
|
|
| _ -> false
|
|
|
|
and group_tuple = function
|
|
| {pat_desc = (Tpat_tuple _|Tpat_any)} -> true
|
|
| _ -> false
|
|
|
|
and group_record = function
|
|
| {pat_desc = (Tpat_record _|Tpat_any)} -> true
|
|
| _ -> false
|
|
|
|
and group_array = function
|
|
| {pat_desc=Tpat_array _} -> true
|
|
| _ -> false
|
|
|
|
let get_group p = match p.pat_desc with
|
|
| Tpat_any -> group_var
|
|
| Tpat_constant _ -> group_constant
|
|
| Tpat_construct (_, _) -> group_constructor
|
|
| Tpat_tuple _ -> group_tuple
|
|
| Tpat_record _ -> group_record
|
|
| Tpat_array _ -> group_array
|
|
| Tpat_variant (_,_,_) -> group_variant
|
|
| _ -> fatal_error "Matching.get_group"
|
|
|
|
|
|
|
|
let is_or p = match p.pat_desc with
|
|
| Tpat_or _ -> true
|
|
| _ -> false
|
|
|
|
(* Conditions for appending to the Or matrix *)
|
|
let conda p q = not (compat p q)
|
|
and condb act ps qs = not (is_guarded act) && Parmatch.le_pats qs ps
|
|
|
|
let or_ok p ps l =
|
|
List.for_all
|
|
(function
|
|
| ({pat_desc=Tpat_or _} as q::qs,act) ->
|
|
conda p q || condb act ps qs
|
|
| _ -> true)
|
|
l
|
|
|
|
(* Insert or append a or-pattern in the Or matrix *)
|
|
|
|
let insert_or_append p ps act ors no =
|
|
let rec attempt seen = function
|
|
| (q::qs,act_q) as cl::rem ->
|
|
if is_or q then begin
|
|
if compat p q then
|
|
if
|
|
IdentSet.is_empty (extract_vars IdentSet.empty p) &&
|
|
IdentSet.is_empty (extract_vars IdentSet.empty q) &&
|
|
equiv_pat p q
|
|
then (* attempt insert, for equivalent orpats with no variables *)
|
|
let _, not_e = get_equiv q rem in
|
|
if
|
|
or_ok p ps not_e && (* check append condition for head of O *)
|
|
List.for_all (* check insert condition for tail of O *)
|
|
(fun cl -> match cl with
|
|
| (q::_,_) -> not (compat p q)
|
|
| _ -> assert false)
|
|
seen
|
|
then (* insert *)
|
|
List.rev_append seen ((p::ps,act)::cl::rem), no
|
|
else (* fail to insert or append *)
|
|
ors,(p::ps,act)::no
|
|
else if condb act_q ps qs then (* check condition (b) for append *)
|
|
attempt (cl::seen) rem
|
|
else
|
|
ors,(p::ps,act)::no
|
|
else (* p # q, go on with append/insert *)
|
|
attempt (cl::seen) rem
|
|
end else (* q is not a or-pat, go on with append/insert *)
|
|
attempt (cl::seen) rem
|
|
| _ -> (* [] in fact *)
|
|
(p::ps,act)::ors,no in (* success in appending *)
|
|
attempt [] ors
|
|
|
|
|
|
let separe argo pm =
|
|
let ex_pat,pm = simplify_matching pm in
|
|
match pm.cases with
|
|
| [[{pat_desc=Tpat_any}],_] ->
|
|
compile_or argo pm.cases [] pm.args pm.default,[]
|
|
| _ ->
|
|
let next,nexts =
|
|
match ex_pat.pat_desc with
|
|
| Tpat_any -> compile_or argo pm.cases [] pm.args pm.default,[]
|
|
| _ ->
|
|
let group = get_group ex_pat in
|
|
|
|
let rec sep_ex yes ors no = function
|
|
| ((p::ps,act) as cl)::rem ->
|
|
if group p then begin
|
|
if up_ok cl no then
|
|
if up_ok cl ors then
|
|
sep_ex (cl::yes) ors no rem
|
|
else if or_ok p ps ors then
|
|
sep_ex yes (cl::ors) no rem
|
|
else
|
|
sep_ex yes ors (cl::no) rem
|
|
else
|
|
sep_ex yes ors (cl::no) rem
|
|
end else if is_or p then begin
|
|
if up_ok cl no then
|
|
let ors,no = insert_or_append p ps act ors no in
|
|
sep_ex yes ors no rem
|
|
else
|
|
sep_ex yes ors (cl::no) rem
|
|
end else (* p is a variable *)
|
|
sep_ex yes ors (cl::no) rem
|
|
| _ -> (* [] in fact *)
|
|
cons_next (List.rev yes) (List.rev ors) (List.rev no)
|
|
|
|
and sep_noex yes no = function
|
|
| [ps,_ as cl]
|
|
when List.for_all group_var ps && yes <> [] ->
|
|
cons_next (List.rev yes) [] (List.rev (cl::no))
|
|
| ((p::_,_) as cl)::rem ->
|
|
if group_var p && up_ok cl no then
|
|
sep_noex (cl::yes) no rem
|
|
else
|
|
sep_noex yes (cl::no) rem
|
|
| _ -> (* [] in fact *)
|
|
cons_next (List.rev yes) [] (List.rev no)
|
|
|
|
and sep_next cl rem = match cl with
|
|
| ((p::_),_) ->
|
|
if group p then
|
|
sep_ex [cl] [] [] rem
|
|
else if is_or p then
|
|
sep_ex [] [cl] [] rem
|
|
else
|
|
sep_noex [cl] [] rem
|
|
| _ -> assert false
|
|
|
|
and cons_next yes yesor = function
|
|
| [] ->
|
|
as_matrix (yes@yesor),
|
|
compile_or argo yes yesor pm.args pm.default,[]
|
|
| cl::rem ->
|
|
let matrix,next,nexts = sep_next cl rem in
|
|
begin match next with
|
|
(* Optimisation of jumps to jumps *)
|
|
| {to_match =
|
|
{cases=[ps,Lstaticraise (idef,[])]} ;
|
|
to_catch=[]}
|
|
when List.for_all group_var ps ->
|
|
let newdef =
|
|
cons_default matrix idef next.to_match.default in
|
|
as_matrix (yes@yesor),
|
|
compile_or argo yes yesor pm.args newdef,
|
|
(-1,next)::nexts
|
|
| _ ->
|
|
let idef = next_raise_count () in
|
|
let newdef =
|
|
cons_default matrix idef next.to_match.default in
|
|
as_matrix (yes@yesor),
|
|
compile_or argo yes yesor pm.args newdef,
|
|
(idef,next)::nexts
|
|
end in
|
|
match pm.cases with
|
|
| ((_::_),_) as cl::rem ->
|
|
let _,next,nexts = sep_next cl rem in
|
|
next, nexts
|
|
| _ ->
|
|
compile_or argo pm.cases [] pm.args pm.default,[] in
|
|
(*
|
|
prerr_endline "SEPARE" ;
|
|
pretty_ext next ;
|
|
List.iter (fun (e,p) -> Printf.eprintf "** %d **\n" e ; flush stderr ; pretty_ext p) nexts ;
|
|
*)
|
|
(next,nexts)
|
|
|
|
|
|
|
|
(* General divide functions *)
|
|
|
|
|
|
let divide make get_key get_args ctx pm =
|
|
|
|
let rec divide_rec = function
|
|
| (p::patl,action) :: rem ->
|
|
let this_match = divide_rec rem in
|
|
add
|
|
(make p pm.default ctx)
|
|
this_match (get_key p) (get_args p patl,action) pm.args
|
|
| _ -> [] in
|
|
|
|
divide_rec pm.cases
|
|
|
|
|
|
let divide_line make_ctx make get_args pat ctx pm =
|
|
let rec divide_rec = function
|
|
| (p::patl,action) :: rem ->
|
|
let this_match = divide_rec rem in
|
|
add_line (get_args p patl, action) this_match
|
|
| _ -> make pm.default pm.args in
|
|
|
|
{pm = divide_rec pm.cases ;
|
|
ctx=make_ctx ctx ;
|
|
pat=pat}
|
|
|
|
|
|
let make_default matcher (exit,l) =
|
|
let rec make_rec = function
|
|
| [] -> []
|
|
| ([[]],i)::_ -> [[[]],i]
|
|
| (pss,i)::rem ->
|
|
let rem = make_rec rem in
|
|
match filter_matrix matcher pss with
|
|
| [] -> rem
|
|
| ([]::_) -> ([[]],i)::rem
|
|
| pss -> (pss,i)::rem in
|
|
exit,make_rec l
|
|
|
|
(* Then come various functions,
|
|
There is one set of functions per matching style
|
|
(constants, constructors etc.)
|
|
|
|
- matcher function are arguments to make_default (for defaukt handlers)
|
|
They may raise NoMatch or OrPat and perform the full
|
|
matching (selection + arguments).
|
|
|
|
|
|
- get_args and get_key are for the compiled matrices, note that
|
|
selection and geting arguments are separed.
|
|
|
|
- make_ _matching combines the previous functions for produicing
|
|
new ``pattern_matching'' records.
|
|
*)
|
|
|
|
|
|
|
|
let rec matcher_const cst p rem = match p.pat_desc with
|
|
| Tpat_or (p1,p2,_) ->
|
|
begin try
|
|
matcher_const cst p1 rem with
|
|
| NoMatch -> matcher_const cst p2 rem
|
|
end
|
|
| Tpat_constant c1 when c1=cst -> rem
|
|
| Tpat_any -> rem
|
|
| _ -> raise NoMatch
|
|
|
|
let get_key_constant caller = function
|
|
| {pat_desc= Tpat_constant cst} as p -> cst
|
|
| p ->
|
|
prerr_endline ("BAD: "^caller) ;
|
|
pretty_pat p ;
|
|
assert false
|
|
|
|
let get_args_constant _ rem = rem
|
|
|
|
let make_constant_matching p def ctx = function
|
|
[] -> fatal_error "Matching.make_constant_matching"
|
|
| (_ :: argl) ->
|
|
let def =
|
|
make_default
|
|
(matcher_const (get_key_constant "make" p)) def
|
|
and ctx =
|
|
filter_ctx p ctx in
|
|
{pm = {cases = []; args = argl ; default = def} ;
|
|
ctx = ctx ;
|
|
pat = normalize_pat p}
|
|
|
|
|
|
|
|
|
|
let divide_constant ctx m =
|
|
divide
|
|
make_constant_matching (get_key_constant "divide")
|
|
get_args_constant
|
|
ctx m
|
|
|
|
(* Matching against a constructor *)
|
|
|
|
|
|
let make_field_args binding_kind arg first_pos last_pos argl =
|
|
let rec make_args pos =
|
|
if pos > last_pos
|
|
then argl
|
|
else (Lprim(Pfield pos, [arg]), binding_kind) :: make_args (pos + 1)
|
|
in make_args first_pos
|
|
|
|
let get_key_constr = function
|
|
| {pat_desc=Tpat_construct (cstr,_)} -> cstr.cstr_tag
|
|
| _ -> assert false
|
|
|
|
let get_args_constr p rem = match p with
|
|
| {pat_desc=Tpat_construct (_,args)} -> args @ rem
|
|
| _ -> assert false
|
|
|
|
let pat_as_constr = function
|
|
| {pat_desc=Tpat_construct (cstr,_)} -> cstr
|
|
| _ -> fatal_error "Matching.pat_as_constr"
|
|
|
|
|
|
let matcher_constr cstr = match cstr.cstr_arity with
|
|
| 0 ->
|
|
let rec matcher_rec q rem = match q.pat_desc with
|
|
| Tpat_or (p1,p2,_) ->
|
|
begin
|
|
try
|
|
matcher_rec p1 rem
|
|
with
|
|
| NoMatch -> matcher_rec p2 rem
|
|
end
|
|
| Tpat_construct (cstr1, []) when cstr.cstr_tag = cstr1.cstr_tag ->
|
|
rem
|
|
| Tpat_any -> rem
|
|
| _ -> raise NoMatch in
|
|
matcher_rec
|
|
| 1 ->
|
|
let rec matcher_rec q rem = match q.pat_desc with
|
|
| Tpat_or (p1,p2,_) ->
|
|
let r1 = try Some (matcher_rec p1 rem) with NoMatch -> None
|
|
and r2 = try Some (matcher_rec p2 rem) with NoMatch -> None in
|
|
begin match r1,r2 with
|
|
| None, None -> raise NoMatch
|
|
| Some r1, None -> r1
|
|
| None, Some r2 -> r2
|
|
| Some (a1::rem1), Some (a2::_) ->
|
|
{a1 with
|
|
pat_loc = Location.none ;
|
|
pat_desc = Tpat_or (a1, a2, None)}::
|
|
rem
|
|
| _, _ -> assert false
|
|
end
|
|
| Tpat_construct (cstr1, [arg]) when cstr.cstr_tag = cstr1.cstr_tag ->
|
|
arg::rem
|
|
| Tpat_any -> omega::rem
|
|
| _ -> raise NoMatch in
|
|
matcher_rec
|
|
| _ ->
|
|
fun q rem -> match q.pat_desc with
|
|
| Tpat_or (_,_,_) -> raise OrPat
|
|
| Tpat_construct (cstr1, args)
|
|
when cstr.cstr_tag = cstr1.cstr_tag -> args @ rem
|
|
| Tpat_any -> Parmatch.omegas cstr.cstr_arity @ rem
|
|
| _ -> raise NoMatch
|
|
|
|
let make_constr_matching p def ctx = function
|
|
[] -> fatal_error "Matching.make_constr_matching"
|
|
| ((arg, mut) :: argl) ->
|
|
let cstr = pat_as_constr p in
|
|
let newargs =
|
|
match cstr.cstr_tag with
|
|
Cstr_constant _ | Cstr_block _ ->
|
|
make_field_args Alias arg 0 (cstr.cstr_arity - 1) argl
|
|
| Cstr_exception _ ->
|
|
make_field_args Alias arg 1 cstr.cstr_arity argl in
|
|
{pm=
|
|
{cases = []; args = newargs;
|
|
default = make_default (matcher_constr cstr) def} ;
|
|
ctx = filter_ctx p ctx ;
|
|
pat=normalize_pat p}
|
|
|
|
|
|
let divide_constructor ctx pm =
|
|
divide
|
|
make_constr_matching
|
|
get_key_constr get_args_constr
|
|
ctx pm
|
|
|
|
(* Matching against a variant *)
|
|
|
|
let rec matcher_variant_const lab p rem = match p.pat_desc with
|
|
| Tpat_or (p1, p2, _) ->
|
|
begin
|
|
try
|
|
matcher_variant_const lab p1 rem
|
|
with
|
|
| NoMatch -> matcher_variant_const lab p2 rem
|
|
end
|
|
| Tpat_variant (lab1,_,_) when lab1=lab -> rem
|
|
| Tpat_any -> rem
|
|
| _ -> raise NoMatch
|
|
|
|
|
|
let make_variant_matching_constant p lab def ctx = function
|
|
[] -> fatal_error "Matching.make_variant_matching_constant"
|
|
| ((arg, mut) :: argl) ->
|
|
let def = make_default (matcher_variant_const lab) def
|
|
and ctx = filter_ctx p ctx in
|
|
{pm={ cases = []; args = argl ; default=def} ;
|
|
ctx=ctx ;
|
|
pat = normalize_pat p}
|
|
|
|
let matcher_variant_nonconst lab p rem = match p.pat_desc with
|
|
| Tpat_or (_,_,_) -> raise OrPat
|
|
| Tpat_variant (lab1,Some arg,_) when lab1=lab -> arg::rem
|
|
| Tpat_any -> omega::rem
|
|
| _ -> raise NoMatch
|
|
|
|
|
|
let make_variant_matching_nonconst p lab def ctx = function
|
|
[] -> fatal_error "Matching.make_variant_matching_nonconst"
|
|
| ((arg, mut) :: argl) ->
|
|
let def = make_default (matcher_variant_nonconst lab) def
|
|
and ctx = filter_ctx p ctx in
|
|
{pm=
|
|
{cases = []; args = (Lprim(Pfield 1, [arg]), Alias) :: argl;
|
|
default=def} ;
|
|
ctx=ctx ;
|
|
pat = normalize_pat p}
|
|
|
|
let get_key_variant p = match p.pat_desc with
|
|
| Tpat_variant(lab, Some _ , _) -> Cstr_block (Btype.hash_variant lab)
|
|
| Tpat_variant(lab, None , _) -> Cstr_constant (Btype.hash_variant lab)
|
|
| _ -> assert false
|
|
|
|
let divide_variant row ctx ({cases = cl; args = al; default=def} as pm) =
|
|
let row = Btype.row_repr row in
|
|
let rec divide = function
|
|
({pat_desc = Tpat_variant(lab, pato, _)} as p:: patl, action) :: rem ->
|
|
let variants = divide rem in
|
|
if try Btype.row_field_repr (List.assoc lab row.row_fields) = Rabsent
|
|
with Not_found -> true
|
|
then
|
|
variants
|
|
else begin
|
|
let tag = Btype.hash_variant lab in
|
|
match pato with
|
|
None ->
|
|
add (make_variant_matching_constant p lab def ctx) variants
|
|
(Cstr_constant tag) (patl, action) al
|
|
| Some pat ->
|
|
add (make_variant_matching_nonconst p lab def ctx) variants
|
|
(Cstr_block tag) (pat :: patl, action) al
|
|
end
|
|
| cl -> []
|
|
in
|
|
divide cl
|
|
|
|
(*
|
|
Three ``no-test'' cases
|
|
*)
|
|
|
|
(* Matching against a variable *)
|
|
|
|
let get_args_var _ rem = rem
|
|
|
|
|
|
let make_var_matching def = function
|
|
| [] -> fatal_error "Matching.make_var_matching"
|
|
| _::argl ->
|
|
{cases=[] ;
|
|
args = argl ;
|
|
default= make_default get_args_var def}
|
|
|
|
let divide_var ctx pm =
|
|
divide_line ctx_lshift make_var_matching get_args_var omega ctx pm
|
|
|
|
(* Matching against a tuple pattern *)
|
|
|
|
|
|
let get_args_tuple arity p rem = match p with
|
|
| {pat_desc = Tpat_any} -> omegas arity @ rem
|
|
| {pat_desc = Tpat_tuple args} ->
|
|
args @ rem
|
|
| _ -> assert false
|
|
|
|
let matcher_tuple arity p rem = match p.pat_desc with
|
|
| Tpat_or (_,_,_) -> raise OrPat
|
|
| Tpat_var _ -> get_args_tuple arity omega rem
|
|
| _ -> get_args_tuple arity p rem
|
|
|
|
let make_tuple_matching arity def = function
|
|
[] -> fatal_error "Matching.make_tuple_matching"
|
|
| (arg, mut) :: argl ->
|
|
let rec make_args pos =
|
|
if pos >= arity
|
|
then argl
|
|
else (Lprim(Pfield pos, [arg]), Alias) :: make_args (pos + 1) in
|
|
{cases = []; args = make_args 0 ;
|
|
default=make_default (matcher_tuple arity) def}
|
|
|
|
|
|
let divide_tuple arity p ctx pm =
|
|
divide_line
|
|
(filter_ctx p)
|
|
(make_tuple_matching arity)
|
|
(get_args_tuple arity) p ctx pm
|
|
|
|
(* Matching against a record pattern *)
|
|
|
|
|
|
let record_matching_line num_fields lbl_pat_list =
|
|
let patv = Array.create num_fields omega in
|
|
List.iter (fun (lbl, pat) -> patv.(lbl.lbl_pos) <- pat) lbl_pat_list;
|
|
Array.to_list patv
|
|
|
|
let get_args_record num_fields p rem = match p with
|
|
| {pat_desc=Tpat_any} ->
|
|
record_matching_line num_fields [] @ rem
|
|
| {pat_desc=Tpat_record lbl_pat_list} ->
|
|
record_matching_line num_fields lbl_pat_list @ rem
|
|
| _ -> assert false
|
|
|
|
let matcher_record num_fields p rem = match p.pat_desc with
|
|
| Tpat_or (_,_,_) -> raise OrPat
|
|
| Tpat_var _ -> get_args_record num_fields omega rem
|
|
| _ -> get_args_record num_fields p rem
|
|
|
|
let make_record_matching all_labels def = function
|
|
[] -> fatal_error "Matching.make_record_matching"
|
|
| ((arg, mut) :: argl) ->
|
|
let rec make_args pos =
|
|
if pos >= Array.length all_labels then argl else begin
|
|
let lbl = all_labels.(pos) in
|
|
let access =
|
|
match lbl.lbl_repres with
|
|
Record_regular -> Pfield lbl.lbl_pos
|
|
| Record_float -> Pfloatfield lbl.lbl_pos in
|
|
let str =
|
|
match lbl.lbl_mut with
|
|
Immutable -> Alias
|
|
| Mutable -> StrictOpt in
|
|
(Lprim(access, [arg]), str) :: make_args(pos + 1)
|
|
end in
|
|
let nfields = Array.length all_labels in
|
|
let def= make_default (matcher_record nfields) def in
|
|
{cases = []; args = make_args 0 ; default = def}
|
|
|
|
|
|
let divide_record all_labels p ctx pm =
|
|
let get_args = get_args_record (Array.length all_labels) in
|
|
divide_line
|
|
(filter_ctx p)
|
|
(make_record_matching all_labels)
|
|
get_args
|
|
p ctx pm
|
|
|
|
(* Matching against an array pattern *)
|
|
|
|
let get_key_array = function
|
|
| {pat_desc=Tpat_array patl} -> List.length patl
|
|
| _ -> assert false
|
|
|
|
let get_args_array p rem = match p with
|
|
| {pat_desc=Tpat_array patl} -> patl@rem
|
|
| _ -> assert false
|
|
|
|
let matcher_array len p rem = match p.pat_desc with
|
|
| Tpat_or (_,_,_) -> raise OrPat
|
|
| Tpat_array args when List.length args=len -> args @ rem
|
|
| Tpat_any -> Parmatch.omegas len @ rem
|
|
| _ -> raise NoMatch
|
|
|
|
let make_array_matching kind p def ctx = function
|
|
| [] -> fatal_error "Matching.make_array_matching"
|
|
| ((arg, mut) :: argl) ->
|
|
let len = get_key_array p in
|
|
let rec make_args pos =
|
|
if pos >= len
|
|
then argl
|
|
else (Lprim(Parrayrefu kind, [arg; Lconst(Const_base(Const_int pos))]),
|
|
StrictOpt) :: make_args (pos + 1) in
|
|
let def = make_default (matcher_array len) def
|
|
and ctx = filter_ctx p ctx in
|
|
{pm={cases = []; args = make_args 0 ; default = def} ;
|
|
ctx=ctx ;
|
|
pat = normalize_pat p}
|
|
|
|
let divide_array kind ctx pm =
|
|
divide
|
|
(make_array_matching kind)
|
|
get_key_array get_args_array ctx pm
|
|
|
|
(* To combine sub-matchings together *)
|
|
|
|
let float_compare s1 s2 =
|
|
let f1 = float_of_string s1 and f2 = float_of_string s2 in
|
|
Pervasives.compare f1 f2
|
|
|
|
let sort_lambda_list l =
|
|
List.sort
|
|
(fun (x,_) (y,_) -> match x,y with
|
|
| Const_float f1, Const_float f2 -> float_compare f1 f2
|
|
| _, _ -> Pervasives.compare x y)
|
|
l
|
|
|
|
let rec cut n l =
|
|
if n = 0 then [],l
|
|
else match l with
|
|
[] -> raise (Invalid_argument "cut")
|
|
| a::l -> let l1,l2 = cut (n-1) l in a::l1, l2
|
|
|
|
let rec do_tests_fail fail tst arg = function
|
|
| [] -> fail
|
|
| (c, act)::rem ->
|
|
Lifthenelse
|
|
(Lprim (tst, [arg ; Lconst (Const_base c)]),
|
|
do_tests_fail fail tst arg rem,
|
|
act)
|
|
|
|
let rec do_tests_nofail tst arg = function
|
|
| [] -> fatal_error "Matching.do_tests_nofail"
|
|
| [_,act] -> act
|
|
| (c,act)::rem ->
|
|
Lifthenelse
|
|
(Lprim (tst, [arg ; Lconst (Const_base c)]),
|
|
do_tests_nofail tst arg rem,
|
|
act)
|
|
|
|
let make_test_sequence fail tst lt_tst arg const_lambda_list =
|
|
let rec make_test_sequence const_lambda_list =
|
|
if List.length const_lambda_list >= 4 && lt_tst <> Praise then
|
|
split_sequence const_lambda_list
|
|
else match fail with
|
|
| None -> do_tests_nofail tst arg const_lambda_list
|
|
| Some fail -> do_tests_fail fail tst arg const_lambda_list
|
|
|
|
and split_sequence const_lambda_list =
|
|
let list1, list2 =
|
|
cut (List.length const_lambda_list / 2) const_lambda_list in
|
|
Lifthenelse(Lprim(lt_tst,[arg; Lconst(Const_base (fst(List.hd list2)))]),
|
|
make_test_sequence list1, make_test_sequence list2)
|
|
in make_test_sequence (sort_lambda_list const_lambda_list)
|
|
|
|
|
|
let make_offset x arg = if x=0 then arg else Lprim(Poffsetint(x), [arg])
|
|
|
|
|
|
|
|
let prim_string_notequal =
|
|
Pccall{prim_name = "string_notequal";
|
|
prim_arity = 2; prim_alloc = false;
|
|
prim_native_name = ""; prim_native_float = false}
|
|
|
|
let rec explode_inter offset i j act k =
|
|
if i <= j then
|
|
explode_inter offset i (j-1) act ((j-offset,act)::k)
|
|
else
|
|
k
|
|
|
|
let max_vals cases acts =
|
|
let vals = Array.create (Array.length acts) 0 in
|
|
for i=Array.length cases-1 downto 0 do
|
|
let l,h,act = cases.(i) in
|
|
vals.(act) <- h - l + 1 + vals.(act)
|
|
done ;
|
|
let max = ref 0 in
|
|
for i = Array.length vals-1 downto 0 do
|
|
if vals.(i) >= vals.(!max) then
|
|
max := i
|
|
done ;
|
|
if vals.(!max) > 1 then
|
|
!max
|
|
else
|
|
-1
|
|
|
|
let as_int_list cases acts =
|
|
let default = max_vals cases acts in
|
|
let min_key,_,_ = cases.(0)
|
|
and _,max_key,_ = cases.(Array.length cases-1) in
|
|
let offset = max_key-min_key in
|
|
let rec do_rec i k =
|
|
if i >= 0 then
|
|
let low, high, act = cases.(i) in
|
|
if act = default then
|
|
do_rec (i-1) k
|
|
else
|
|
do_rec (i-1) (explode_inter min_key low high acts.(act) k)
|
|
else
|
|
k in
|
|
min_key, max_key,do_rec (Array.length cases-1) [],
|
|
(if default >= 0 then Some acts.(default) else None)
|
|
|
|
|
|
let make_switch_offset arg min_key max_key int_lambda_list default =
|
|
let numcases = max_key - min_key + 1 in
|
|
let cases =
|
|
List.map (fun (key, l) -> (key - min_key, l)) int_lambda_list in
|
|
let offsetarg = make_offset (-min_key) arg in
|
|
Lswitch(offsetarg,
|
|
{sw_numconsts = numcases; sw_consts = cases;
|
|
sw_numblocks = 0; sw_blocks = [];
|
|
sw_failaction = default})
|
|
|
|
let make_switch_switcher arg cases acts =
|
|
let l = ref [] in
|
|
for i = Array.length cases-1 downto 0 do
|
|
l := (i,acts.(cases.(i))) :: !l
|
|
done ;
|
|
Lswitch(arg,
|
|
{sw_numconsts = Array.length cases ; sw_consts = !l ;
|
|
sw_numblocks = 0 ; sw_blocks = [] ;
|
|
sw_failaction = None})
|
|
|
|
let full sw =
|
|
List.length sw.sw_consts = sw.sw_numconsts &&
|
|
List.length sw.sw_blocks = sw.sw_numblocks
|
|
|
|
let make_switch (arg,sw) = match sw.sw_failaction with
|
|
| None ->
|
|
let t = Hashtbl.create 17 in
|
|
let seen l = match l with
|
|
| Lstaticraise (i,[]) ->
|
|
let old = try Hashtbl.find t i with Not_found -> 0 in
|
|
Hashtbl.replace t i (old+1)
|
|
| _ -> () in
|
|
List.iter (fun (_,lam) -> seen lam) sw.sw_consts ;
|
|
List.iter (fun (_,lam) -> seen lam) sw.sw_blocks ;
|
|
let i_max = ref (-1)
|
|
and max = ref (-1) in
|
|
Hashtbl.iter
|
|
(fun i c ->
|
|
if c > !max then begin
|
|
i_max := i ;
|
|
max := c
|
|
end) t ;
|
|
if !i_max >= 0 then
|
|
let default = !i_max in
|
|
let rec remove = function
|
|
| [] -> []
|
|
| (_,Lstaticraise (j,[]))::rem when j=default ->
|
|
remove rem
|
|
| x::rem -> x::remove rem in
|
|
Lswitch
|
|
(arg,
|
|
{sw with
|
|
sw_consts = remove sw.sw_consts ;
|
|
sw_blocks = remove sw.sw_blocks ;
|
|
sw_failaction = Some (Lstaticraise (default,[]))})
|
|
else
|
|
Lswitch (arg,sw)
|
|
| _ -> Lswitch (arg,sw)
|
|
|
|
module SArg = struct
|
|
type primitive = Lambda.primitive
|
|
|
|
let eqint = Pintcomp Ceq
|
|
let neint = Pintcomp Cneq
|
|
let leint = Pintcomp Cle
|
|
let ltint = Pintcomp Clt
|
|
let geint = Pintcomp Cge
|
|
let gtint = Pintcomp Cgt
|
|
|
|
type act = Lambda.lambda
|
|
|
|
let make_prim p args = Lprim (p,args)
|
|
let make_offset arg n = match n with
|
|
| 0 -> arg
|
|
| _ -> Lprim (Poffsetint n,[arg])
|
|
let bind arg body =
|
|
let newvar,newarg = match arg with
|
|
| Lvar v -> v,arg
|
|
| _ ->
|
|
let newvar = Ident.create "switcher" in
|
|
newvar,Lvar newvar in
|
|
bind Alias newvar arg (body newarg)
|
|
|
|
let make_isout h arg = Lprim (Pisout, [h ; arg])
|
|
let make_isin h arg = Lprim (Pnot,[make_isout h arg])
|
|
let make_if cond ifso ifnot = Lifthenelse (cond, ifso, ifnot)
|
|
let make_switch = make_switch_switcher
|
|
end
|
|
|
|
module Switcher = Switch.Make(SArg)
|
|
open Switch
|
|
|
|
let lambda_of_int i = Lconst (Const_base (Const_int i))
|
|
|
|
let rec last def = function
|
|
| [] -> def
|
|
| [x,_] -> x
|
|
| _::rem -> last def rem
|
|
|
|
let get_edges low high l = match l with
|
|
| [] -> low, high
|
|
| (x,_)::_ -> x, last high l
|
|
|
|
|
|
let as_interval_canfail fail low high l =
|
|
let store = mk_store equal_action in
|
|
let rec nofail_rec cur_low cur_high cur_act = function
|
|
| [] ->
|
|
if cur_high = high then
|
|
[cur_low,cur_high,cur_act]
|
|
else
|
|
[(cur_low,cur_high,cur_act) ; (cur_high+1,high, 0)]
|
|
| ((i,act_i)::rem) as all ->
|
|
let act_index = store.act_store act_i in
|
|
if cur_high+1= i then
|
|
if act_index=cur_act then
|
|
nofail_rec cur_low i cur_act rem
|
|
else if act_index=0 then
|
|
(cur_low,i-1, cur_act)::fail_rec i i rem
|
|
else
|
|
(cur_low, i-1, cur_act)::nofail_rec i i act_index rem
|
|
else
|
|
(cur_low, cur_high, cur_act)::
|
|
fail_rec ((cur_high+1)) (cur_high+1) all
|
|
|
|
and fail_rec cur_low cur_high = function
|
|
| [] -> [(cur_low, cur_high, 0)]
|
|
| (i,act_i)::rem ->
|
|
let index = store.act_store act_i in
|
|
if index=0 then fail_rec cur_low i rem
|
|
else
|
|
(cur_low,i-1,0)::
|
|
nofail_rec i i index rem in
|
|
|
|
let rec init_rec = function
|
|
| [] -> []
|
|
| (i,act_i)::rem as all ->
|
|
let index = store.act_store act_i in
|
|
if index=0 then
|
|
fail_rec low i rem
|
|
else
|
|
if low < i then
|
|
(low,i-1,0)::nofail_rec i i index rem
|
|
else
|
|
nofail_rec i i index rem in
|
|
|
|
ignore (store.act_store fail) ; (* fail has action index 0 *)
|
|
let r = init_rec l in
|
|
Array.of_list r, store.act_get ()
|
|
|
|
let as_interval_nofail l =
|
|
let store = mk_store equal_action in
|
|
|
|
let rec i_rec cur_low cur_high cur_act = function
|
|
| [] ->
|
|
[cur_low, cur_high, cur_act]
|
|
| (i,act)::rem ->
|
|
let act_index = store.act_store act in
|
|
if act_index = cur_act then
|
|
i_rec cur_low i cur_act rem
|
|
else
|
|
(cur_low, cur_high, cur_act)::
|
|
i_rec i i act_index rem in
|
|
let inters = match l with
|
|
| (i,act)::rem ->
|
|
let act_index = store.act_store act in
|
|
i_rec i i act_index rem
|
|
| _ -> assert false in
|
|
|
|
Array.of_list inters, store.act_get ()
|
|
|
|
|
|
let sort_int_lambda_list l =
|
|
List.sort
|
|
(fun (i1,_) (i2,_) ->
|
|
if i1 < i2 then -1
|
|
else if i2 < i1 then 1
|
|
else 0)
|
|
l
|
|
|
|
let as_interval fail low high l =
|
|
let l = sort_int_lambda_list l in
|
|
get_edges low high l,
|
|
(match fail with
|
|
| None -> as_interval_nofail l
|
|
| Some act -> as_interval_canfail act low high l)
|
|
|
|
let call_switcher konst fail arg low high int_lambda_list =
|
|
let edges, (cases, actions) =
|
|
as_interval fail low high int_lambda_list in
|
|
Switcher.zyva edges konst arg cases actions
|
|
|
|
|
|
let exists_ctx ok ctx =
|
|
List.exists
|
|
(function
|
|
| {right=p::_} -> ok p
|
|
| _ -> assert false)
|
|
ctx
|
|
|
|
let rec list_as_pat = function
|
|
| [] -> fatal_error "Matching.list_as_pat"
|
|
| [pat] -> pat
|
|
| pat::rem ->
|
|
{pat with pat_desc = Tpat_or (pat,list_as_pat rem,None)}
|
|
|
|
|
|
let rec pat_as_list k = function
|
|
| {pat_desc=Tpat_or (p1,p2,_)} ->
|
|
pat_as_list (pat_as_list k p2) p1
|
|
| p -> p::k
|
|
|
|
(* Extracting interesting patterns *)
|
|
exception All
|
|
|
|
let rec extract_pat seen k p = match p.pat_desc with
|
|
| Tpat_or (p1,p2,_) ->
|
|
let k1,seen1 = extract_pat seen k p1 in
|
|
extract_pat seen1 k1 p2
|
|
| Tpat_alias (p,_) ->
|
|
extract_pat seen k p
|
|
| Tpat_var _|Tpat_any ->
|
|
raise All
|
|
| _ ->
|
|
let q = normalize_pat p in
|
|
if List.exists (compat q) seen then
|
|
k, seen
|
|
else
|
|
q::k, q::seen
|
|
|
|
let extract_mat seen pss =
|
|
let r,_ =
|
|
List.fold_left
|
|
(fun (k,seen) ps -> match ps with
|
|
| p::_ -> extract_pat seen k p
|
|
| _ -> assert false)
|
|
([],seen)
|
|
pss in
|
|
r
|
|
|
|
|
|
|
|
let complete_pats_constrs = function
|
|
| p::_ as pats ->
|
|
List.map
|
|
(pat_of_constr p)
|
|
(complete_constrs p (List.map get_key_constr pats))
|
|
| _ -> assert false
|
|
|
|
|
|
let mk_res get_key env last_choice idef cant_fail ctx =
|
|
|
|
let env,fail,jumps_fail = match last_choice with
|
|
| [] ->
|
|
env, None, jumps_empty
|
|
| [p] when group_var p ->
|
|
env,
|
|
Some (Lstaticraise (idef,[])),
|
|
jumps_singleton idef ctx
|
|
| _ ->
|
|
(idef,cant_fail,last_choice)::env,
|
|
None, jumps_empty in
|
|
let klist,jumps =
|
|
List.fold_right
|
|
(fun (i,cant_fail,pats) (klist,jumps) ->
|
|
let act = Lstaticraise (i,[])
|
|
and pat = list_as_pat pats in
|
|
let klist =
|
|
List.fold_right
|
|
(fun pat klist -> (get_key pat,act)::klist)
|
|
pats klist
|
|
and ctx = if cant_fail then ctx else ctx_lub pat ctx in
|
|
klist,jumps_add i ctx jumps)
|
|
env ([],jumps_fail) in
|
|
fail, klist, jumps
|
|
|
|
|
|
(* Aucune optimisation, reflechir apres la release *)
|
|
let mk_failaction_neg partial ctx (_,def) = match partial with
|
|
| Partial -> begin match def with
|
|
| (_,idef)::_ ->
|
|
Some (Lstaticraise (idef,[])),[],jumps_singleton idef ctx
|
|
| __ -> assert false
|
|
end
|
|
| Total ->
|
|
None, [], jumps_empty
|
|
|
|
|
|
(* Conforme a l'article et plus simple qu'avant *)
|
|
and mk_failaction_pos partial seen ctx (_,defs) =
|
|
let rec scan_def env to_test defs = match to_test,defs with
|
|
| ([],_)|(_,[]) ->
|
|
List.fold_left
|
|
(fun (klist,jumps) (pats,i)->
|
|
let action = Lstaticraise (i,[]) in
|
|
let klist =
|
|
List.fold_right
|
|
(fun pat r -> (get_key_constr pat,action)::r)
|
|
pats klist
|
|
and jumps =
|
|
jumps_add i (ctx_lub (list_as_pat pats) ctx) jumps in
|
|
klist,jumps)
|
|
([],jumps_empty) env
|
|
| _,(pss,idef)::rem ->
|
|
let now, later =
|
|
List.partition
|
|
(fun (p,p_ctx) -> ctx_match p_ctx pss) to_test in
|
|
match now with
|
|
| [] -> scan_def env to_test rem
|
|
| _ -> scan_def ((List.map fst now,idef)::env) later rem in
|
|
|
|
scan_def
|
|
[]
|
|
(List.map
|
|
(fun pat -> pat, ctx_lub pat ctx)
|
|
(complete_pats_constrs seen))
|
|
defs
|
|
|
|
|
|
let combine_constant arg cst partial ctx def
|
|
(const_lambda_list, total, pats) =
|
|
let fail, to_add, local_jumps =
|
|
mk_failaction_neg partial ctx def in
|
|
let const_lambda_list = to_add@const_lambda_list in
|
|
let lambda1 =
|
|
match cst with
|
|
| Const_int _ ->
|
|
let int_lambda_list =
|
|
List.map (function Const_int n, l -> n,l | _ -> assert false)
|
|
const_lambda_list in
|
|
call_switcher
|
|
lambda_of_int fail arg min_int max_int int_lambda_list
|
|
| Const_char _ ->
|
|
let int_lambda_list =
|
|
List.map (function Const_char c, l -> (Char.code c, l)
|
|
| _ -> assert false)
|
|
const_lambda_list in
|
|
call_switcher
|
|
(fun i -> Lconst (Const_base (Const_int i)))
|
|
fail arg 0 255 int_lambda_list
|
|
| Const_string _ ->
|
|
make_test_sequence
|
|
fail prim_string_notequal Praise arg const_lambda_list
|
|
| Const_float _ ->
|
|
make_test_sequence
|
|
fail
|
|
(Pfloatcomp Cneq) (Pfloatcomp Clt)
|
|
arg const_lambda_list
|
|
| Const_int32 _ ->
|
|
make_test_sequence
|
|
fail
|
|
(Pbintcomp(Pint32, Cneq)) (Pbintcomp(Pint32, Clt))
|
|
arg const_lambda_list
|
|
| Const_int64 _ ->
|
|
make_test_sequence
|
|
fail
|
|
(Pbintcomp(Pint64, Cneq)) (Pbintcomp(Pint64, Clt))
|
|
arg const_lambda_list
|
|
| Const_nativeint _ ->
|
|
make_test_sequence
|
|
fail
|
|
(Pbintcomp(Pnativeint, Cneq)) (Pbintcomp(Pnativeint, Clt))
|
|
arg const_lambda_list
|
|
in lambda1,jumps_union local_jumps total
|
|
|
|
|
|
|
|
let split_cases tag_lambda_list =
|
|
let rec split_rec = function
|
|
[] -> ([], [])
|
|
| (cstr, act) :: rem ->
|
|
let (consts, nonconsts) = split_rec rem in
|
|
match cstr with
|
|
Cstr_constant n -> ((n, act) :: consts, nonconsts)
|
|
| Cstr_block n -> (consts, (n, act) :: nonconsts)
|
|
| _ -> assert false in
|
|
let const, nonconst = split_rec tag_lambda_list in
|
|
sort_int_lambda_list const,
|
|
sort_int_lambda_list nonconst
|
|
|
|
|
|
let combine_constructor arg ex_pat cstr partial ctx def
|
|
(tag_lambda_list, total1, pats) =
|
|
if cstr.cstr_consts < 0 then begin
|
|
(* Special cases for exceptions *)
|
|
let cstrs = List.map fst tag_lambda_list in
|
|
let fail, to_add, local_jumps =
|
|
mk_failaction_neg partial ctx def in
|
|
let tag_lambda_list = to_add@tag_lambda_list in
|
|
let lambda1 =
|
|
let default, tests =
|
|
match fail with
|
|
| None ->
|
|
begin match tag_lambda_list with
|
|
| (_, act)::rem -> act,rem
|
|
| _ -> assert false
|
|
end
|
|
| Some fail -> fail, tag_lambda_list in
|
|
List.fold_right
|
|
(fun (ex, act) rem ->
|
|
match ex with
|
|
| Cstr_exception path ->
|
|
Lifthenelse(Lprim(Pintcomp Ceq,
|
|
[Lprim(Pfield 0, [arg]); transl_path path]),
|
|
act, rem)
|
|
| _ -> assert false)
|
|
tests default in
|
|
lambda1, jumps_union local_jumps total1
|
|
end else begin
|
|
(* Regular concrete type *)
|
|
let ncases = List.length tag_lambda_list
|
|
and nconstrs = cstr.cstr_consts + cstr.cstr_nonconsts in
|
|
let sig_complete = ncases = nconstrs
|
|
and cstrs = List.map fst tag_lambda_list in
|
|
let fails,local_jumps =
|
|
if sig_complete then [],jumps_empty
|
|
else
|
|
mk_failaction_pos
|
|
partial pats ctx def in
|
|
|
|
let tag_lambda_list = fails @ tag_lambda_list in
|
|
let (consts, nonconsts) = split_cases tag_lambda_list in
|
|
let lambda1 =
|
|
match same_actions tag_lambda_list with
|
|
| Some act -> act
|
|
| _ ->
|
|
match
|
|
(cstr.cstr_consts, cstr.cstr_nonconsts, consts, nonconsts)
|
|
with
|
|
| (1, 1, [0, act1], [0, act2]) ->
|
|
Lifthenelse(arg, act2, act1)
|
|
| (n,_,_,[]) ->
|
|
call_switcher
|
|
(fun i -> Lconst (Const_base (Const_int i)))
|
|
None arg 0 (n-1) consts
|
|
| (n, _, _, _) ->
|
|
match same_actions nonconsts with
|
|
| None ->
|
|
make_switch(arg, {sw_numconsts = cstr.cstr_consts;
|
|
sw_consts = consts;
|
|
sw_numblocks = cstr.cstr_nonconsts;
|
|
sw_blocks = nonconsts;
|
|
sw_failaction = None})
|
|
| Some act ->
|
|
Lifthenelse
|
|
(Lprim (Pisint, [arg]),
|
|
call_switcher
|
|
(fun i -> Lconst (Const_base (Const_int i)))
|
|
None arg
|
|
0 (n-1) consts,
|
|
act) in
|
|
lambda1, jumps_union local_jumps total1
|
|
end
|
|
|
|
let make_test_sequence_variant_constant fail arg int_lambda_list =
|
|
let _, (cases, actions) =
|
|
as_interval fail min_int max_int int_lambda_list in
|
|
Switcher.test_sequence
|
|
(fun i -> Lconst (Const_base (Const_int i))) arg cases actions
|
|
|
|
let call_switcher_variant_constant fail arg int_lambda_list =
|
|
call_switcher
|
|
(fun i -> Lconst (Const_base (Const_int i)))
|
|
fail arg min_int max_int int_lambda_list
|
|
|
|
|
|
let call_switcher_variant_constr fail arg int_lambda_list =
|
|
let v = Ident.create "variant" in
|
|
Llet(Alias, v, Lprim(Pfield 0, [arg]),
|
|
call_switcher
|
|
(fun i -> Lconst (Const_base (Const_int i)))
|
|
fail (Lvar v) min_int max_int int_lambda_list)
|
|
|
|
let combine_variant row arg partial ctx def (tag_lambda_list, total1, pats) =
|
|
let row = Btype.row_repr row in
|
|
let num_constr = ref 0 in
|
|
if row.row_closed then
|
|
List.iter
|
|
(fun (_, f) ->
|
|
match Btype.row_field_repr f with
|
|
Rabsent | Reither(true, _::_, _, _) -> ()
|
|
| _ -> incr num_constr)
|
|
row.row_fields
|
|
else
|
|
num_constr := max_int;
|
|
let test_int_or_block arg if_int if_block =
|
|
Lifthenelse(Lprim (Pisint, [arg]), if_int, if_block) in
|
|
let sig_complete = List.length tag_lambda_list = !num_constr
|
|
and one_action = same_actions tag_lambda_list in
|
|
let fail, to_add, local_jumps =
|
|
if sig_complete || (match partial with Total -> true | _ -> false) then
|
|
None, [], jumps_empty
|
|
else
|
|
mk_failaction_neg partial ctx def in
|
|
let tag_lambda_list = to_add@tag_lambda_list in
|
|
let (consts, nonconsts) = split_cases tag_lambda_list in
|
|
let lambda1 = match fail, one_action with
|
|
| None, Some act -> act
|
|
| _,_ ->
|
|
match (consts, nonconsts) with
|
|
| ([n, act1], [m, act2]) when fail=None ->
|
|
test_int_or_block arg act1 act2
|
|
| (_, []) -> (* One can compare integers and pointers *)
|
|
make_test_sequence_variant_constant fail arg consts
|
|
| ([], _) ->
|
|
let lam = call_switcher_variant_constr
|
|
fail arg nonconsts in
|
|
(* One must not dereference integers *)
|
|
begin match fail with
|
|
| None -> lam
|
|
| Some fail -> test_int_or_block arg fail lam
|
|
end
|
|
| (_, _) ->
|
|
let lam_const =
|
|
call_switcher_variant_constant
|
|
fail arg consts
|
|
and lam_nonconst =
|
|
call_switcher_variant_constr
|
|
fail arg nonconsts in
|
|
test_int_or_block arg lam_const lam_nonconst
|
|
in
|
|
lambda1, jumps_union local_jumps total1
|
|
|
|
|
|
let combine_array arg kind partial ctx def
|
|
(len_lambda_list, total1, pats) =
|
|
let fail, to_add, local_jumps = mk_failaction_neg partial ctx def in
|
|
let len_lambda_list = to_add @ len_lambda_list in
|
|
let lambda1 =
|
|
let newvar = Ident.create "len" in
|
|
let switch =
|
|
call_switcher
|
|
lambda_of_int
|
|
fail (Lvar newvar)
|
|
0 max_int len_lambda_list in
|
|
bind
|
|
Alias newvar (Lprim(Parraylength kind, [arg])) switch in
|
|
lambda1, jumps_union local_jumps total1
|
|
|
|
(* Insertion of debugging events *)
|
|
|
|
let rec event_branch repr lam =
|
|
begin match lam, repr with
|
|
(_, None) ->
|
|
lam
|
|
| (Levent(lam', ev), Some r) ->
|
|
incr r;
|
|
Levent(lam', {lev_pos = ev.lev_pos;
|
|
lev_kind = ev.lev_kind;
|
|
lev_repr = repr;
|
|
lev_env = ev.lev_env})
|
|
| (Llet(str, id, lam, body), _) ->
|
|
Llet(str, id, lam, event_branch repr body)
|
|
| Lstaticraise _,_ -> lam
|
|
| (_, Some r) ->
|
|
Printlambda.lambda Format.str_formatter lam ;
|
|
fatal_error
|
|
("Matching.event_branch: "^Format.flush_str_formatter ())
|
|
end
|
|
|
|
|
|
exception Unused
|
|
|
|
let compile_list compile_fun division =
|
|
|
|
let rec c_rec totals = function
|
|
| [] -> [], jumps_unions totals, []
|
|
| (key, cell) :: rem ->
|
|
begin match cell.ctx with
|
|
| [] -> c_rec totals rem
|
|
| _ ->
|
|
try
|
|
let (lambda1, total1) = compile_fun cell.ctx cell.pm in
|
|
let c_rem, total, new_pats =
|
|
c_rec
|
|
(jumps_map ctx_combine total1::totals) rem in
|
|
((key,lambda1)::c_rem), total, (cell.pat::new_pats)
|
|
with
|
|
| Unused -> c_rec totals rem
|
|
end in
|
|
c_rec [] division
|
|
|
|
|
|
let compile_orhandlers compile_fun lambda1 total1 ctx to_catch =
|
|
let rec do_rec r total_r = function
|
|
| [] -> r,total_r
|
|
| (mat,i,vars,pm)::rem ->
|
|
begin try
|
|
let ctx = select_columns mat ctx in
|
|
let handler_i, total_i = compile_fun ctx pm in
|
|
match raw_action r with
|
|
| Lstaticraise (j,args) ->
|
|
if i=j then
|
|
List.fold_right2 (bind Alias) vars args handler_i,
|
|
jumps_map (ctx_rshift_num (ncols mat)) total_i
|
|
else
|
|
do_rec r total_r rem
|
|
| _ ->
|
|
do_rec
|
|
(Lstaticcatch (r,(i,vars), handler_i))
|
|
(jumps_union
|
|
(jumps_remove i total_r)
|
|
(jumps_map (ctx_rshift_num (ncols mat)) total_i))
|
|
rem
|
|
with
|
|
| Unused ->
|
|
do_rec (Lstaticcatch (r, (i,vars), lambda_unit)) total_r rem
|
|
end in
|
|
do_rec lambda1 total1 to_catch
|
|
|
|
|
|
let compile_test compile_fun partial divide combine ctx to_match to_catch =
|
|
let division = divide ctx to_match in
|
|
let c_div = compile_list compile_fun division in
|
|
match c_div with
|
|
| [],_,_ ->
|
|
begin match mk_failaction_neg partial ctx to_match.default with
|
|
| None,_,_ -> raise Unused
|
|
| Some l,_,total -> l,total
|
|
end
|
|
| _ ->
|
|
let lambda1,total1 =
|
|
combine ctx to_match.default c_div in
|
|
compile_orhandlers compile_fun lambda1 total1 ctx to_catch
|
|
|
|
|
|
(* Attempt to avoid some useless bindinds by lowering them *)
|
|
|
|
(* Approximation of v present in lam *)
|
|
let rec approx_present v = function
|
|
| Lconst _ -> false
|
|
| Lstaticraise (_,args) ->
|
|
List.exists (fun lam -> approx_present v lam) args
|
|
| Lprim (_,args) ->
|
|
List.exists (fun lam -> approx_present v lam) args
|
|
| Llet (Alias, _, l1, l2) ->
|
|
approx_present v l1 || approx_present v l2
|
|
| Lvar vv -> Ident.same v vv
|
|
| _ -> true
|
|
|
|
let string_of_lam lam =
|
|
Printlambda.lambda Format.str_formatter lam ;
|
|
Format.flush_str_formatter ()
|
|
|
|
let rec lower_bind v arg lam = match lam with
|
|
| Lifthenelse (cond, ifso, ifnot) ->
|
|
let pcond = approx_present v cond
|
|
and pso = approx_present v ifso
|
|
and pnot = approx_present v ifnot in
|
|
begin match pcond, pso, pnot with
|
|
| false, false, false -> lam
|
|
| false, true, false ->
|
|
Lifthenelse (cond, lower_bind v arg ifso, ifnot)
|
|
| false, false, true ->
|
|
Lifthenelse (cond, ifso, lower_bind v arg ifnot)
|
|
| _,_,_ -> bind Alias v arg lam
|
|
end
|
|
| Lswitch (ls,({sw_consts=[i,act] ; sw_blocks = []} as sw))
|
|
when not (approx_present v ls) ->
|
|
Lswitch (ls, {sw with sw_consts = [i,lower_bind v arg act]})
|
|
| Lswitch (ls,({sw_consts=[] ; sw_blocks = [i,act]} as sw))
|
|
when not (approx_present v ls) ->
|
|
Lswitch (ls, {sw with sw_blocks = [i,lower_bind v arg act]})
|
|
| Llet (Alias, vv, lv, l) ->
|
|
if approx_present v lv then
|
|
bind Alias v arg lam
|
|
else
|
|
Llet (Alias, vv, lv, lower_bind v arg l)
|
|
| _ ->
|
|
bind Alias v arg lam
|
|
|
|
let bind_check str v arg lam = match str,arg with
|
|
| _, Lvar _ ->bind str v arg lam
|
|
| Alias,_ -> lower_bind v arg lam
|
|
| _,_ -> bind str v arg lam
|
|
|
|
let rec comp_exit ctx m =
|
|
match m.default with
|
|
| exit,(_,i)::_ ->
|
|
Lstaticraise (i,[]), jumps_singleton i ctx
|
|
| _ -> fatal_error "Matching.comp_exit"
|
|
|
|
|
|
|
|
let rec comp_match_handlers comp_fun partial ctx arg first_match next_matchs = match next_matchs with
|
|
| [] -> comp_fun partial ctx arg first_match
|
|
| rem ->
|
|
let rec c_rec body total_body = function
|
|
| [] -> body, total_body
|
|
(* Hum, -1 means never taken, needed for ``partial'' to be correct *)
|
|
| (-1,pm)::rem -> c_rec body total_body rem
|
|
| (i,pm)::rem ->
|
|
let ctx_i,total_rem = jumps_extract i total_body in
|
|
begin match ctx_i with
|
|
| [] -> c_rec body total_body rem
|
|
| _ ->
|
|
try
|
|
let li,total_i =
|
|
comp_fun
|
|
(match rem with [] -> partial | _ -> Partial)
|
|
ctx_i arg pm in
|
|
c_rec
|
|
(Lstaticcatch (body,(i,[]),li))
|
|
(jumps_union total_i total_rem)
|
|
rem
|
|
with
|
|
| Unused ->
|
|
c_rec (Lstaticcatch (body,(i,[]),lambda_unit))
|
|
total_rem rem
|
|
end in
|
|
try
|
|
let first_lam,total = comp_fun Partial ctx arg first_match in
|
|
c_rec first_lam total rem
|
|
with Unused -> match next_matchs with
|
|
| [] -> raise Unused
|
|
| (_,x)::xs -> comp_match_handlers comp_fun partial ctx arg x xs
|
|
|
|
|
|
(*
|
|
The main compilation function.
|
|
Input:
|
|
repr=used for inserting debug events
|
|
partial=exhaustiveness information from Parmatch
|
|
ctx=a context
|
|
m=a pattern matching
|
|
|
|
Output: a lambda term, a jump summary {..., exit number -> context, .. }
|
|
*)
|
|
|
|
|
|
let rec compile_match repr partial ctx m = match m with
|
|
| { cases = [] } -> comp_exit ctx m
|
|
| { cases = ([], action) :: rem } ->
|
|
if is_guarded action then begin
|
|
let (lambda, total) =
|
|
compile_match None partial ctx { m with cases = rem } in
|
|
event_branch repr (patch_guarded lambda action), total
|
|
end else
|
|
(event_branch repr action, jumps_empty)
|
|
| { args = (arg, str)::argl } ->
|
|
let v,newarg =
|
|
match arg with
|
|
| Lvar v -> v,arg
|
|
| _ ->
|
|
let v = name_pattern "match" m.cases in
|
|
v,Lvar v in
|
|
let first_match,rem =
|
|
separe (Some v)
|
|
{ m with args = (newarg, Alias) :: argl } in
|
|
let (lam, total) =
|
|
comp_match_handlers
|
|
(do_compile_matching repr) partial ctx newarg first_match rem in
|
|
bind_check str v arg lam, total
|
|
| _ -> assert false
|
|
|
|
|
|
(* verbose version of do_compile_matching, for debug *)
|
|
and do_compile_matching_pr repr partial ctx arg x =
|
|
prerr_string "COMPILE: " ;
|
|
prerr_endline (match partial with Partial -> "Partial" | Total -> "Total") ;
|
|
prerr_endline "MATCH" ;
|
|
pretty_ext x ;
|
|
prerr_endline "CTX" ;
|
|
pretty_ctx ctx ;
|
|
let (_, jumps) as r = do_compile_matching repr partial ctx arg x in
|
|
prerr_endline "JUMPS" ;
|
|
pretty_jumps jumps ;
|
|
r
|
|
|
|
and do_compile_matching repr partial ctx arg
|
|
{to_match=to_match; to_catch=to_catch} =
|
|
|
|
let pat = what_is_cases to_match.cases in
|
|
match pat.pat_desc with
|
|
| Tpat_any ->
|
|
assert (to_catch=[]) ;
|
|
compile_no_test divide_var ctx_rshift repr partial ctx to_match to_catch
|
|
| Tpat_tuple patl ->
|
|
compile_no_test
|
|
(divide_tuple (List.length patl) (normalize_pat pat)) ctx_combine
|
|
repr partial ctx to_match to_catch
|
|
| Tpat_record ((lbl,_)::_) ->
|
|
compile_no_test
|
|
(divide_record lbl.lbl_all (normalize_pat pat))
|
|
ctx_combine repr partial ctx to_match to_catch
|
|
| Tpat_constant cst ->
|
|
compile_test
|
|
(compile_match repr partial) partial
|
|
divide_constant
|
|
(combine_constant arg cst partial)
|
|
ctx to_match to_catch
|
|
| Tpat_construct (cstr, _) ->
|
|
compile_test
|
|
(compile_match repr partial) partial
|
|
divide_constructor (combine_constructor arg pat cstr partial)
|
|
ctx to_match to_catch
|
|
| Tpat_array _ ->
|
|
let kind = Typeopt.array_pattern_kind pat in
|
|
compile_test (compile_match repr partial) partial
|
|
(divide_array kind) (combine_array arg kind partial)
|
|
ctx to_match to_catch
|
|
| Tpat_variant(lab, _, row) ->
|
|
compile_test (compile_match repr partial) partial
|
|
(divide_variant row)
|
|
(combine_variant row arg partial)
|
|
ctx to_match to_catch
|
|
| _ ->
|
|
fatal_error "Matching.do_compile_matching"
|
|
|
|
and compile_no_test divide up_ctx repr partial ctx to_match to_catch =
|
|
let {pm=this_match ; ctx=this_ctx } = divide ctx to_match in
|
|
let lambda,total = compile_match repr partial this_ctx this_match in
|
|
let total = jumps_map up_ctx total in
|
|
compile_orhandlers (compile_match repr partial) lambda total ctx to_catch
|
|
|
|
|
|
|
|
|
|
(* The entry points *)
|
|
|
|
|
|
(* had toplevel handler when appropriate *)
|
|
|
|
let start_ctx n = [{left=[] ; right = omegas n}]
|
|
|
|
let check_total total lambda i handler_fun =
|
|
if jumps_is_empty total then
|
|
lambda
|
|
else begin
|
|
Lstaticcatch(lambda, (i,[]), handler_fun())
|
|
end
|
|
|
|
let compile_matching loc repr handler_fun arg pat_act_list partial =
|
|
match partial with
|
|
| Partial ->
|
|
let raise_num = next_raise_count () in
|
|
let pm =
|
|
{ cases = List.map (fun (pat, act) -> ([pat], act)) pat_act_list;
|
|
args = [arg, Strict] ;
|
|
default = raise_num,[[[omega]],raise_num]} in
|
|
begin try
|
|
let (lambda, total) = compile_match repr partial (start_ctx 1) pm in
|
|
check_total total lambda raise_num handler_fun
|
|
with
|
|
| Unused -> handler_fun()
|
|
end
|
|
| Total ->
|
|
let pm =
|
|
{ cases = List.map (fun (pat, act) -> ([pat], act)) pat_act_list;
|
|
args = [arg, Strict] ;
|
|
default = (-1,[])} in
|
|
let (lambda, total) = compile_match repr partial (start_ctx 1) pm in
|
|
assert (jumps_is_empty total) ;
|
|
lambda
|
|
|
|
let partial_function loc () =
|
|
(* [Location.get_pos_info] is too expensive *)
|
|
let fname = match loc.Location.loc_start.Lexing.pos_fname with
|
|
| "" -> !Location.input_name
|
|
| x -> x
|
|
in
|
|
let pos = loc.Location.loc_start in
|
|
let line = pos.Lexing.pos_lnum in
|
|
let char = pos.Lexing.pos_cnum - pos.Lexing.pos_bol in
|
|
Lprim(Praise, [Lprim(Pmakeblock(0, Immutable),
|
|
[transl_path Predef.path_match_failure;
|
|
Lconst(Const_block(0,
|
|
[Const_base(Const_string fname);
|
|
Const_base(Const_int line);
|
|
Const_base(Const_int char)]))])])
|
|
|
|
let for_function loc repr param pat_act_list partial =
|
|
compile_matching loc repr (partial_function loc) param pat_act_list partial
|
|
|
|
(* In the following two cases, exhaustiveness info is not available! *)
|
|
let for_trywith param pat_act_list =
|
|
compile_matching Location.none None (fun () -> Lprim(Praise, [param]))
|
|
param pat_act_list Partial
|
|
|
|
let for_let loc param pat body =
|
|
compile_matching loc None (partial_function loc) param [pat, body] Partial
|
|
|
|
(* Handling of tupled functions and matches *)
|
|
|
|
|
|
let flatten_pattern size p =
|
|
match p.pat_desc with
|
|
Tpat_tuple args -> args
|
|
| Tpat_any -> omegas size
|
|
| _ -> raise Cannot_flatten
|
|
|
|
|
|
|
|
let rec flatten_pat_line size p k = match p.pat_desc with
|
|
| Tpat_any -> omegas size::k
|
|
| Tpat_tuple args -> args::k
|
|
| Tpat_or (p1,p2,_) ->
|
|
flatten_pat_line size p1 (flatten_pat_line size p2 k)
|
|
| _ -> fatal_error "Matching.flatten_pat_line"
|
|
|
|
let flatten_cases size cases =
|
|
List.map
|
|
(fun (ps,action) -> match ps with
|
|
| [p] -> flatten_pattern size p,action
|
|
| _ -> fatal_error "Matching.flatten_case")
|
|
cases
|
|
|
|
let flatten_matrix size pss =
|
|
List.fold_right
|
|
(fun ps r -> match ps with
|
|
| [p] -> flatten_pat_line size p r
|
|
| _ -> fatal_error "Matching.flatten_matrix")
|
|
pss []
|
|
|
|
let flatten_def size (exit,def) =
|
|
exit,
|
|
List.map
|
|
(fun (pss,i) -> flatten_matrix size pss,i)
|
|
def
|
|
|
|
let flatten_pm size al pm =
|
|
{args = al ; cases = flatten_cases size pm.cases ;
|
|
default = flatten_def size pm.default}
|
|
|
|
let flatten_extended size idl ext =
|
|
{to_match = flatten_pm size idl ext.to_match ;
|
|
to_catch =
|
|
List.map
|
|
(fun (mat,i,vars,pm) -> flatten_matrix size mat,i,vars,pm)
|
|
ext.to_catch}
|
|
|
|
let compile_flattened repr partial ctx _
|
|
{to_match=to_match ; to_catch=to_catch} =
|
|
let lambda,total = compile_match repr partial ctx to_match in
|
|
compile_orhandlers (compile_match repr partial) lambda total ctx to_catch
|
|
|
|
let for_tupled_function loc paraml pats_act_list partial =
|
|
let raise_num = next_raise_count () in
|
|
let omegas = [List.map (fun _ -> omega) paraml] in
|
|
let pm =
|
|
{ cases = pats_act_list;
|
|
args = List.map (fun id -> (Lvar id, Strict)) paraml ;
|
|
default = raise_num,[omegas,raise_num]
|
|
} in
|
|
try
|
|
let (lambda, total) = compile_match None partial
|
|
(start_ctx (List.length paraml)) pm in
|
|
check_total total lambda raise_num (partial_function loc)
|
|
with
|
|
| Unused -> partial_function loc ()
|
|
|
|
let for_multiple_match loc paraml pat_act_list partial =
|
|
let repr = None in
|
|
let raise_num,pm1 = match partial with
|
|
| Partial ->
|
|
let raise_num = next_raise_count () in
|
|
raise_num,
|
|
{ cases = List.map (fun (pat, act) -> ([pat], act)) pat_act_list;
|
|
args = [Lprim(Pmakeblock(0, Immutable), paraml), Strict] ;
|
|
default = raise_num,[[[omega]],raise_num] }
|
|
| _ ->
|
|
-1,
|
|
{ cases = List.map (fun (pat, act) -> ([pat], act)) pat_act_list;
|
|
args = [Lprim(Pmakeblock(0, Immutable), paraml), Strict] ;
|
|
default = -1,[] } in
|
|
|
|
try
|
|
try
|
|
|
|
let next,nexts = separe None pm1 in
|
|
|
|
let size = List.length paraml in
|
|
let idl = List.map (fun _ -> Ident.create "match") paraml in
|
|
let al = List.map (fun id -> (Lvar id, Alias)) idl in
|
|
let omegas = [List.map (fun _ -> omega) idl] in
|
|
let flat_next = flatten_extended size al next
|
|
and flat_nexts =
|
|
List.map (fun (i,x) -> i,flatten_extended size al x) nexts in
|
|
|
|
|
|
let lambda,total =
|
|
comp_match_handlers
|
|
(compile_flattened repr)
|
|
partial (start_ctx size) staticfail flat_next flat_nexts in
|
|
List.fold_right2 (bind Strict) idl paraml
|
|
(match partial with
|
|
| Partial ->
|
|
check_total total lambda raise_num (partial_function loc)
|
|
| Total ->
|
|
assert (jumps_is_empty total) ;
|
|
lambda)
|
|
|
|
|
|
with Cannot_flatten ->
|
|
let (lambda, total) = compile_match None partial (start_ctx 1) pm1 in
|
|
begin match partial with
|
|
| Partial ->
|
|
check_total total lambda raise_num (partial_function loc)
|
|
| Total ->
|
|
assert (jumps_is_empty total) ;
|
|
lambda
|
|
end
|
|
with Unused ->
|
|
partial_function loc ()
|
|
|