blob: ade0ec336cb241bd0538e426678eb3ee15182849 [file] [edit]
(*
This transformation totalizes partial functions.
Partial functions are recognized by the partial flag hint (for now, inference
would be possible).
The declarations are changed:
* the return type is wrapped in the option type `?`
* all clauses rhs' are wrapped in the option type injection `?(…)`
* a catch-all clause is added returning `null`
All calls to such functions are wrapped in option projection `THE e`.
*)
open Util
open Source
open Il.Ast
(* Errors *)
let _error at msg = Error.error at "totality" msg
(* Environment *)
module S = Set.Make(String)
type env =
{ mutable partial_funs : S.t;
}
let new_env () : env =
{ partial_funs = S.empty;
}
let is_partial (env : env) (id : id) = S.mem id.it env.partial_funs
let register_partial (env : env) (id :id) =
env.partial_funs <- S.add id.it env.partial_funs
(* Transformation *)
(* The main transformation case *)
let rec t_exp env exp =
let exp' = t_exp2 env exp in
match exp'.it with
| CallE (f, _) when is_partial env f ->
{exp' with it = TheE {exp' with note = IterT (exp'.note, Opt) $ exp'.at}}
| _ -> exp'
(* Type traversal *)
and t_typ env x = { x with it = t_typ' env x.it }
and t_typ' env = function
| VarT (id, args) -> VarT (id, t_args env args)
| (BoolT | NumT _ | TextT) as t -> t
| TupT xts -> TupT (List.map (fun (id, t) -> (id, t_typ env t)) xts)
| IterT (t, iter) -> IterT (t_typ env t, t_iter env iter)
and t_deftyp env x = { x with it = t_deftyp' env x.it }
and t_deftyp' env = function
| AliasT t -> AliasT (t_typ env t)
| StructT typfields -> StructT (List.map (t_typfield env) typfields)
| VariantT typcases -> VariantT (List.map (t_typcase env) typcases)
and t_typfield env (atom, (t, quants, prems), hints) =
(atom, (t_typ env t, t_params env quants, t_prems env prems), hints)
and t_typcase env (atom, (t, quants, prems), hints) =
(atom, (t_typ env t, t_params env quants, t_prems env prems), hints)
(* Expr traversal *)
and t_exp2 env x = { x with it = t_exp' env x.it; note = t_typ env x.note }
and t_exp' env = function
| (VarE _ | BoolE _ | NumE _ | TextE _) as e -> e
| UnE (unop, nto, exp) -> UnE (unop, nto, t_exp env exp)
| BinE (binop, nto, exp1, exp2) -> BinE (binop, nto, t_exp env exp1, t_exp env exp2)
| CmpE (cmpop, nto, exp1, exp2) -> CmpE (cmpop, nto, t_exp env exp1, t_exp env exp2)
| IdxE (exp1, exp2) -> IdxE (t_exp env exp1, t_exp env exp2)
| SliceE (exp1, exp2, exp3) -> SliceE (t_exp env exp1, t_exp env exp2, t_exp env exp3)
| UpdE (exp1, path, exp2) -> UpdE (t_exp env exp1, t_path env path, t_exp env exp2)
| ExtE (exp1, path, exp2) -> ExtE (t_exp env exp1, t_path env path, t_exp env exp2)
| StrE fields -> StrE (List.map (fun (a, e) -> a, t_exp env e) fields)
| DotE (e, a) -> DotE (t_exp env e, a)
| CompE (exp1, exp2) -> CompE (t_exp env exp1, t_exp env exp2)
| LenE exp -> LenE (t_exp env exp)
| TupE es -> TupE (List.map (t_exp env) es)
| CallE (a, args) -> CallE (a, List.map (t_arg env) args)
| IterE (e, iterexp) -> IterE (t_exp env e, t_iterexp env iterexp)
| ProjE (e, i) -> ProjE (t_exp env e, i)
| UncaseE (e, mixop) -> UncaseE (t_exp env e, mixop)
| OptE None -> OptE None
| OptE (Some exp) -> OptE (Some (t_exp env exp))
| TheE exp -> TheE (t_exp env exp)
| ListE es -> ListE (List.map (t_exp env) es)
| LiftE exp -> LiftE (t_exp env exp)
| CatE (exp1, exp2) -> CatE (t_exp env exp1, t_exp env exp2)
| MemE (exp1, exp2) -> MemE (t_exp env exp1, t_exp env exp2)
| CaseE (mixop, e) -> CaseE (mixop, t_exp env e)
| CvtE (exp, t1, t2) -> CvtE (t_exp env exp, t1, t2)
| SubE (exp, t1, t2) -> SubE (t_exp env exp, t_typ env t1, t_typ env t2)
and t_iter env = function
| ListN (e, id_opt) -> ListN (t_exp env e, id_opt)
| i -> i
and t_iterexp env (iter, xes) =
(t_iter env iter, List.map (fun (x, e) -> x, t_exp env e) xes)
and t_path' env = function
| RootP -> RootP
| IdxP (path, e) -> IdxP (t_path env path, t_exp env e)
| SliceP (path, e1, e2) -> SliceP (t_path env path, t_exp env e1, t_exp env e2)
| DotP (path, a) -> DotP (t_path env path, a)
and t_path env x = { x with it = t_path' env x.it; note = t_typ env x.note }
and t_sym' env = function
| VarG (id, args) -> VarG (id, t_args env args)
| (NumG _ | TextG _ | EpsG) as g -> g
| SeqG syms -> SeqG (List.map (t_sym env) syms)
| AltG syms -> AltG (List.map (t_sym env) syms)
| RangeG (sym1, sym2) -> RangeG (t_sym env sym1, t_sym env sym2)
| IterG (sym, iter) -> IterG (t_sym env sym, t_iterexp env iter)
| AttrG (e, sym) -> AttrG (t_exp env e, t_sym env sym)
and t_sym env x = { x with it = t_sym' env x.it }
and t_arg' env = function
| ExpA exp -> ExpA (t_exp env exp)
| TypA t -> TypA (t_typ env t)
| DefA id -> DefA id
| GramA sym -> GramA (t_sym env sym)
and t_arg env x = { x with it = t_arg' env x.it }
and t_param' env = function
| ExpP (id, t) -> ExpP (id, t_typ env t)
| TypP id -> TypP id
| DefP (id, ps, t) -> DefP (id, t_params env ps, t_typ env t)
| GramP (id, ps, t) -> GramP (id, t_params env ps, t_typ env t)
and t_param env x = { x with it = t_param' env x.it }
and t_args env = List.map (t_arg env)
and t_params env = List.map (t_param env)
and t_prem' env = function
| RulePr (id, args, mixop, exp) -> RulePr (id, t_args env args, mixop, t_exp env exp)
| IfPr e -> IfPr (t_exp env e)
| LetPr (e1, e2, ids) -> LetPr (t_exp env e1, t_exp env e2, ids)
| ElsePr -> ElsePr
| IterPr (prem, iterexp) -> IterPr (t_prem env prem, t_iterexp env iterexp)
and t_prem env x = { x with it = t_prem' env x.it }
and t_prems env = List.map (t_prem env)
let t_clause' env = function
| DefD (params, lhs, rhs, prems) ->
DefD (t_params env params, t_args env lhs, t_exp env rhs, t_prems env prems)
let t_clause env (clause : clause) = { clause with it = t_clause' env clause.it }
let t_inst' env = function
| InstD (params, args, deftyp) ->
InstD (t_params env params, t_args env args, t_deftyp env deftyp)
let t_inst env (inst : inst) = { inst with it = t_inst' env inst.it }
let t_insts env = List.map (t_inst env)
let t_prod' env = function
| ProdD (params, lhs, rhs, prems) ->
ProdD (t_params env params, t_sym env lhs, t_exp env rhs, t_prems env prems)
let t_prod env (prod : prod) = { prod with it = t_prod' env prod.it }
let t_rule' env = function
| RuleD (id, params, mixop, exp, prems) ->
RuleD (id, t_params env params, mixop, t_exp env exp, t_prems env prems)
let t_rule env x = { x with it = t_rule' env x.it }
let rec t_def' env = function
| RecD defs -> RecD (List.map (t_def env) defs)
| DecD (id, params, typ, clauses) ->
let params' = t_params env params in
let typ' = t_typ env typ in
let clauses' = List.map (t_clause env) clauses in
if is_partial env id then
let typ'' = IterT (typ', Opt) $ no_region in
let clauses'' = List.map (fun clause -> match clause.it with
DefD (params, lhs, rhs, prems) ->
{ clause with
it = DefD (t_params env params, lhs, OptE (Some rhs) $$ no_region % typ'', prems) }
) clauses' in
let params, args = List.mapi (fun i param -> match param.it with
| ExpP (_, typI) ->
let x = ("x" ^ string_of_int i) $ no_region in
[ExpP (x, typI) $ x.at], ExpA (VarE x $$ no_region % typI) $ no_region
| TypP id -> [], TypA (VarT (id, []) $ no_region) $ no_region
| DefP (id, _, _) -> [], DefA id $ no_region
| GramP (id, _, _) -> [], GramA (VarG (id, []) $ no_region) $ no_region
) params' |> List.split in
let catch_all = DefD (List.concat params, args,
OptE None $$ no_region % typ'', []) $ no_region in
DecD (id, params', typ'', clauses'' @ [ catch_all ])
else
DecD (id, params', typ', clauses')
| TypD (id, params, insts) ->
TypD (id, t_params env params, t_insts env insts)
| RelD (id, params, mixop, typ, rules) ->
RelD (id, t_params env params, mixop, t_typ env typ, List.map (t_rule env) rules)
| GramD (id, params, typ, prods) ->
GramD (id, t_params env params, typ, List.map (t_prod env) prods)
| HintD _ as def -> def
and t_def env x = { x with it = t_def' env x.it }
let is_partial_hint hint = hint.hintid.it = "partial"
let register_hints env def =
match def.it with
| HintD {it = DecH (id, hints); _} when List.exists is_partial_hint hints ->
register_partial env id
| _ -> ()
let transform (defs : script) =
let env = new_env () in
List.iter (register_hints env) defs;
List.map (t_def env) defs