blob: 7a0fe7eb651b0b50ccd440ee4b3bd6e9611b3922 [file] [log] [blame] [edit]
open Util
open Source
open Ast
open Xl
open Print
(* Errors *)
let error at msg = Error.error at "validation" msg
(* Environment *)
let find_field fs atom at =
match List.find_opt (fun (atom', _, _) -> Eq.eq_atom atom' atom) fs with
| Some (_, x, _) -> x
| None -> error at ("unbound field `" ^ string_of_atom atom ^ "`")
let find_case cases op at =
match List.find_opt (fun (op', _, _) -> Eq.eq_mixop op' op) cases with
| Some (_, x, _) -> x
| None -> error at ("unknown case `" ^ string_of_mixop op ^ "`")
let typ_string env t =
let t' = Eval.reduce_typ env t in
if Eq.eq_typ t t' then
"`" ^ string_of_typ t ^ "`"
else
"`" ^ string_of_typ t ^ "` = `" ^ string_of_typ t' ^ "`"
(* Type Accessors *)
let expand_typ (env : Env.t) t = (Eval.reduce_typ env t).it
let expand_typdef (env : Env.t) t = (Eval.reduce_typdef env t).it
type direction = Infer | Check
let as_error at phrase dir t expected =
match dir with
| Infer ->
error at (
phrase ^ "'s type `" ^ string_of_typ t ^
"` does not match expected type `" ^ expected ^ "`"
)
| Check ->
error at (
phrase ^ "'s type does not match expected type `" ^
string_of_typ t ^ "`"
)
let as_iter_typ iter phrase env dir t at : typ =
match expand_typ env t with
| IterT (t1, iter2) when iter = iter2 -> t1
| _ -> as_error at phrase dir t ("(_)" ^ string_of_iter iter)
let as_list_typ phrase env dir t at : typ =
match expand_typ env t with
| IterT (t1, (List | List1 | ListN _)) -> t1
| _ -> as_error at phrase dir t "(_)*"
let as_tup_typ phrase env dir t at : (id * typ) list =
match expand_typ env t with
| TupT xts -> xts
| _ -> as_error at phrase dir t "(_,...,_)"
let as_struct_typ phrase env dir t at : typfield list =
match expand_typdef env t with
| StructT tfs -> tfs
| _ -> as_error at phrase dir t "{...}"
let as_variant_typ phrase env dir t at : typcase list =
match expand_typdef env t with
| VariantT tcs -> tcs
| _ -> as_error at phrase dir t "| ..."
let rec as_comp_typ phrase env dir t at =
match expand_typdef env t with
| AliasT {it = IterT _; _} -> ()
| StructT tfs ->
List.iter (fun (_, (t, _, _), _) -> as_comp_typ phrase env dir t at) tfs
| _ ->
error at (phrase ^ "'s type `" ^ string_of_typ t ^ "` is not composable")
let proj_tup_typ i xts e at =
let rec loop i xts s =
match i, xts with
| _, [] -> error at "invalid tuple projection"
| 0, (_, tI)::_ -> Subst.subst_typ s tI
| i, (xI, tI)::xts' ->
let eI = ProjE (e, i) $$ at % Subst.subst_typ s tI in
loop (i - 1) xts' (Subst.add_varid s xI eI)
in loop i xts Subst.empty
(* Type Equivalence and Subtyping *)
let equiv_typ env t1 t2 at =
if not (Eval.equiv_typ env t1 t2) then
error at ("expression's type " ^ typ_string env t1 ^ " " ^
"does not equal expected type " ^ typ_string env t2)
let sub_typ env t1 t2 at =
if not (Eval.sub_typ env t1 t2) then
error at ("expression's type " ^ typ_string env t1 ^ " " ^
"does not match expected supertype " ^ typ_string env t2)
(* Operators *)
let infer_unop at op ot =
match op, ot with
| #Bool.unop, #Bool.typ -> BoolT, BoolT
| #Num.unop as op, (#Num.typ as nt) ->
if not (Num.typ_unop op nt nt) then
error at ("illegal type " ^ string_of_numtyp nt ^ " for unary operator");
NumT nt, NumT nt
| _, _ ->
error at ("malformed unary operator annotation")
let infer_binop at op ot =
match op, ot with
| #Bool.binop, #Bool.typ -> BoolT, BoolT, BoolT
| #Num.binop as op, (#Num.typ as nt) ->
let nt1, nt2, nt3 =
match op with
| `AddOp | `SubOp | `MulOp | `DivOp | `ModOp -> nt, nt, nt
| `PowOp -> nt, (if nt = `NatT then `NatT else `IntT), nt
in
if not (Num.typ_binop op nt1 nt2 nt3) then
error at ("illegal type " ^ string_of_numtyp nt ^ " for unary operator");
NumT nt1, NumT nt2, NumT nt3
| _, _ ->
error at ("malformed binary operator annotation")
let infer_cmpop at op ot =
match op, ot with
| #Bool.cmpop, #Bool.typ -> None
| #Num.cmpop, (#Num.typ as nt) -> Some (NumT nt)
| _, _ -> error at ("malformed comparison operator annotation")
(* Atoms and Mixops *)
let valid_atom _env atom =
if atom.note.Atom.def = "" then
error atom.at ("missing definition name for atom " ^ Atom.to_string atom)
let valid_mixop env mixop =
Mixop.iter_atoms (valid_atom env) mixop
let check_mixops phrase item list at =
let _, dups =
List.fold_right (fun op (set, dups) ->
let s = Print.string_of_mixop op in
Free.Set.(if mem s set then (set, s::dups) else (add s set, dups))
) list (Free.Set.empty, [])
in
if dups <> [] then
error at (phrase ^ " contains duplicate " ^ item ^ "(s) `" ^
String.concat "`, `" dups ^ "`")
(* Iteration *)
let valid_list valid_x_y env xs ys at =
if List.length xs <> List.length ys then
error at ("arity mismatch for expression list, expected " ^
string_of_int (List.length ys) ^ ", got " ^ string_of_int (List.length xs));
List.iter2 (valid_x_y env) xs ys
let rec valid_binders valid_x env xs : Env.t =
match xs with
| [] -> env
| x::xs -> valid_binders valid_x (valid_x env x) xs
let rec valid_iter ?(side = `Rhs) env iter : Env.t =
match iter with
| Opt | List | List1 -> env
| ListN (e, id_opt) ->
valid_exp ~side env e (NumT `NatT $ e.at);
Option.fold id_opt ~none:env ~some:(fun id ->
Env.bind_var env id (NumT `NatT $ e.at)
)
and valid_iterexp ?(side = `Rhs) env (it, xes) at : iter * Env.t =
Debug.(log_at "il.valid_iterexp" at
(fun _ -> il_iter it)
(fun (it', _) -> il_iter it')
) @@ fun _ ->
let env' = valid_iter ~side env it in
if xes = [] && it <= List1 && side = `Rhs then error at "empty iteration";
let it' = match it with Opt -> Opt | _ -> List in
it',
List.fold_left (fun env' (x, e) ->
let t = infer_exp env e in
valid_exp ~side env e t;
let t1 = as_iter_typ it' "iterator" env Check t e.at in
Env.bind_var env' x t1
) env' xes
(* Types *)
and valid_typ env t = ignore (valid_typ_bind env t)
and valid_typ_bind env t : Env.t =
Debug.(log_at "il.valid_typ" t.at
(fun _ -> fmt "%s" (il_typ t)) (Fun.const "ok")
) @@ fun _ ->
match t.it with
| VarT (id, as_) ->
let ps, _insts = Env.find_typ env id in
ignore (valid_args env as_ ps Subst.empty t.at);
env
| BoolT
| NumT _
| TextT ->
env
| TupT [] ->
env
| TupT ((x1, t1)::xts) ->
valid_typ env t1;
valid_typ_bind (Env.bind_var env x1 t1) (TupT xts $ t.at)
| IterT (t1, iter) ->
match iter with
| ListN (e, _) -> error e.at "definite iterator not allowed in type"
| _ ->
let env' = valid_iter env iter in
valid_typ env' t1;
env
and valid_deftyp env dt =
match dt.it with
| AliasT t ->
valid_typ env t
| StructT tfs ->
check_mixops "record" "field" (List.map (fun (atom, _, _) -> Mixop.Atom atom) tfs) dt.at;
List.iter (valid_typfield env) tfs
| VariantT tcs ->
check_mixops "variant" "case" (List.map (fun (op, _, _) -> op) tcs) dt.at;
List.iter (valid_typcase env) tcs
and valid_typfield env (atom, (t, qs, prems), _hints) =
valid_atom env atom;
let env' = valid_typ_bind env t in
let env'' = valid_quants env' qs in
List.iter (valid_prem env'') prems
and valid_typcase env (mixop, (t, qs, prems), _hints) =
Debug.(log_at "il.valid_typcase" t.at
(fun _ -> fmt "%s" (il_typ t))
(fun _ -> "ok")
) @@ fun _ ->
let arity =
match t.it with
| TupT ts -> List.length ts
| _ -> 1
in
if Mixop.arity mixop <> arity then
error t.at ("inconsistent arity in mixin notation, `" ^ string_of_mixop mixop ^
"` applied to " ^ typ_string env t);
valid_mixop env mixop;
let env' = valid_typ_bind env t in
let env'' = valid_quants env' qs in
List.iter (valid_prem env'') prems
(* Expressions *)
and infer_exp (env : Env.t) e : typ =
Debug.(log_at "il.infer_exp" e.at
(fun _ -> fmt "%s : %s" (il_exp e) (il_typ e.note))
(fun r -> fmt "%s" (il_typ r))
) @@ fun _ ->
match e.it with
| VarE x -> Env.find_var env x
| BoolE _ -> BoolT $ e.at
| NumE n -> NumT (Num.to_typ n) $ e.at
| TextE _ -> TextT $ e.at
| UnE (op, ot, _) -> let _t1, t' = infer_unop e.at op ot in t' $ e.at
| BinE (op, ot, _, _) -> let _t1, _t2, t' = infer_binop e.at op ot in t' $ e.at
| CmpE _ | MemE _ -> BoolT $ e.at
| IdxE (e1, _) -> as_list_typ "expression" env Infer (infer_exp env e1) e1.at
| SliceE (e1, _, _)
| UpdE (e1, _, _)
| ExtE (e1, _, _)
| CompE (e1, _) -> infer_exp env e1
| StrE _ -> error e.at "cannot infer type of record"
| DotE (e1, atom) ->
let tfs = as_struct_typ "expression" env Infer (infer_exp env e1) e1.at in
let t, _qs, _prems = find_field tfs atom e1.at in
t
| TupE es ->
TupT (List.map (fun eI -> "_" $ eI.at, infer_exp env eI) es) $ e.at
| CallE (x, as_) ->
let ps, t, _ = Env.find_def env x in
let s = valid_args env as_ ps Subst.empty e.at in
Subst.subst_typ s t
| IterE (e1, ite) ->
let it, env' = valid_iterexp env ite e.at in
IterT (infer_exp env' e1, it) $ e.at
| ProjE (e1, i) ->
let t1 = infer_exp env e1 in
let xts = as_tup_typ "expression" env Infer t1 e1.at in
proj_tup_typ i xts e1 e.at
| UncaseE (e1, op) ->
let t1 = infer_exp env e1 in
(match as_variant_typ "expression" env Infer t1 e1.at with
| [(op', (t, _, _), _)] when Eq.eq_mixop op op' -> t
| _ -> error e.at "invalid case projection";
)
| OptE _ -> error e.at "cannot infer type of option"
| TheE e1 -> as_iter_typ Opt "option" env Check (infer_exp env e1) e1.at
| ListE es ->
(match List.map (infer_exp env) es with
| [] -> error e.at "cannot infer type of list"
| t :: ts ->
if List.for_all (Eq.eq_typ t) ts then
IterT (t, List) $ e.at
else
error e.at "cannot infer type of list"
)
| LiftE e1 ->
let t1 = as_iter_typ Opt "lifting" env Check (infer_exp env e1) e1.at in
IterT (t1, List) $ e.at
| LenE _ -> NumT `NatT $ e.at
| CatE (e1, e2) ->
let t1 = infer_exp env e1 in
let t2 = infer_exp env e2 in
if Eq.eq_typ t1 t2 then
t1
else
error e.at "cannot infer type of concatenation"
| CaseE _ -> e.note (* error e.at "cannot infer type of case constructor" *)
| CvtE (_, _, t2) -> NumT t2 $ e.at
| SubE (_, _, t2) -> t2
and valid_exp ?(side = `Rhs) env e t =
Debug.(log_at "il.valid_exp" e.at
(fun _ -> fmt "%s :%s %s == %s" (il_exp e) (il_side side) (il_typ e.note) (il_typ t))
(Fun.const "ok")
) @@ fun _ ->
valid_typ env t;
match e.it with
| VarE x when x.it = "_" && side = `Lhs -> ()
| VarE x ->
let t' = Env.find_var env x in
equiv_typ env t' t e.at
| BoolE _ | NumE _ | TextE _ ->
let t' = infer_exp env e in
equiv_typ env t' t e.at
| UnE (op, nt, e1) ->
let t1, t' = infer_unop e.at op nt in
valid_exp ~side env e1 (t1 $ e.at);
equiv_typ env (t' $ e.at) t e.at
| BinE ((`AddOp | `SubOp) as op, nt, e1, ({it = NumE (`Nat _); _} as e2))
| BinE ((`AddOp | `SubOp) as op, nt, ({it = NumE (`Nat _); _} as e1), e2) when side = `Lhs ->
let t1, t2, t' = infer_binop e.at op nt in
valid_exp ~side env e1 (t1 $ e.at);
valid_exp ~side env e2 (t2 $ e.at);
equiv_typ env (t' $ e.at) t e.at
| BinE (op, nt, e1, e2) ->
let t1, t2, t' = infer_binop e.at op nt in
valid_exp env e1 (t1 $ e.at);
valid_exp env e2 (t2 $ e.at);
equiv_typ env (t' $ e.at) t e.at
| CmpE (op, nt, e1, e2) ->
let t' =
match infer_cmpop e.at op nt with
| Some t' -> t' $ e.at
| None -> try infer_exp env e1 with _ -> infer_exp env e2
in
let side' = if op = `EqOp then `Lhs else `Rhs in (* HACK *)
valid_exp ~side:side' env e1 t';
valid_exp ~side:side' env e2 t';
equiv_typ env (BoolT $ e.at) t e.at
| IdxE (e1, e2) ->
let t1 = infer_exp env e1 in
let t' = as_list_typ "expression" env Infer t1 e1.at in
valid_exp env e1 t1;
valid_exp env e2 (NumT `NatT $ e2.at);
equiv_typ env t' t e.at
| SliceE (e1, e2, e3) ->
let _typ' = as_list_typ "expression" env Check t e1.at in
valid_exp env e1 t;
valid_exp env e2 (NumT `NatT $ e2.at);
valid_exp env e3 (NumT `NatT $ e3.at)
| UpdE (e1, p, e2) ->
valid_exp env e1 t;
let t2 = valid_path env p t in
valid_exp env e2 t2
| ExtE (e1, p, e2) ->
valid_exp env e1 t;
let t2 = valid_path env p t in
let _typ21 = as_list_typ "path" env Check t2 p.at in
valid_exp env e2 t2
| StrE efs ->
let tfs = as_struct_typ "record" env Check t e.at in
valid_list (valid_expfield ~side) env efs tfs e.at
| DotE (e1, atom) ->
let t1 = infer_exp env e1 in
valid_exp env e1 t1;
valid_atom env atom;
let tfs = as_struct_typ "expression" env Check t1 e1.at in
let t', _qs, _prems = find_field tfs atom e1.at in
equiv_typ env t' t e.at
| CompE (e1, e2) ->
let _ = as_comp_typ "expression" env Check t e.at in
valid_exp env e1 t;
valid_exp env e2 t
| MemE (e1, e2) ->
let t2 = infer_exp env e2 in
let t1 = as_list_typ "expression" env Check t2 e2.at in
valid_exp env e1 t1;
valid_exp env e2 t2;
equiv_typ env (BoolT $ e.at) t e.at
| LenE e1 ->
let t1 = infer_exp env e1 in
let _typ11 = as_list_typ "expression" env Infer t1 e1.at in
valid_exp env e1 t1;
equiv_typ env (NumT `NatT $ e.at) t e.at
| TupE es ->
let xts = as_tup_typ "tuple" env Check t e.at in
let rec loop i es xts s =
match es, xts with
| [], [] -> ()
| eI::es', (xI, tI)::xts' ->
valid_exp ~side env eI (Subst.subst_typ s tI);
let s' = Subst.add_varid s xI eI in
loop (i + 1) es' xts' s'
| _, _ ->
error e.at ("arity mismatch for tuple, expected " ^
string_of_int (i + List.length xts) ^ ", got " ^
string_of_int (i + List.length es));
in loop 0 es xts Subst.empty
| CallE (x, as_) ->
let ps, t', _ = Env.find_def env x in
let s = valid_args env as_ ps Subst.empty e.at in
equiv_typ env (Subst.subst_typ s t') t e.at
| IterE (e1, ite) ->
let it, env' = valid_iterexp ~side env ite e.at in
let t1 = as_iter_typ it "iteration" env Check t e.at in
valid_exp ~side env' e1 t1
| ProjE (e1, i) ->
let t1 = infer_exp env e1 in
let xts = as_tup_typ "expression" env Infer t1 e1.at in
let side' = if List.length xts > 1 then `Rhs else side in
valid_exp ~side:side' env e1 (TupT xts $ t1.at);
equiv_typ env (proj_tup_typ i xts e1 e.at) t e.at
| UncaseE (e1, op) ->
let t1 = infer_exp env e1 in
valid_exp ~side env e1 t1;
valid_mixop env op;
(match as_variant_typ "expression" env Infer t1 e1.at with
| [(op', (t', _, _), _)] when Eq.eq_mixop op op' ->
equiv_typ env t' t e.at
| _ -> error e.at "invalid case projection";
)
| OptE eo ->
let t1 = as_iter_typ Opt "option" env Check t e.at in
Option.iter (fun e1 -> valid_exp ~side env e1 t1) eo
| TheE e1 ->
valid_exp ~side env e1 (IterT (t, Opt) $ e1.at)
| ListE es ->
let t1 = as_iter_typ List "list" env Check t e.at in
List.iter (fun eI -> valid_exp ~side env eI t1) es
| LiftE e1 ->
let t1 = as_iter_typ List "lifting" env Check t e.at in
valid_exp ~side env e1 (IterT (t1, Opt) $ e1.at)
| CatE (e1, ({it = ListE _ | CatE ({it = ListE _; _}, _); _} as e2))
| CatE (({it = ListE _ | CatE ({it = ListE _; _}, _); _} as e1), e2) when side = `Lhs ->
let _typ1 = as_iter_typ List "list" env Check t e.at in
valid_exp ~side env e1 t;
valid_exp ~side env e2 t
| CatE (e1, e2) ->
let _typ1 = as_iter_typ List "list" env Check t e.at in
valid_exp env e1 t;
valid_exp env e2 t
| CaseE (op, e1) ->
let cases = as_variant_typ "case" env Check t e.at in
let t1, _qs, _prems = find_case cases op e1.at in
valid_mixop env op;
valid_exp ~side env e1 t1
| CvtE (e1, nt1, nt2) ->
valid_exp ~side env e1 (NumT nt1 $ e1.at);
equiv_typ env (NumT nt2 $e.at) t e.at;
| SubE (e1, t1, t2) ->
valid_typ env t1;
valid_typ env t2;
valid_exp ~side env e1 t1;
equiv_typ env t2 t e.at;
sub_typ env t1 t2 e.at
and valid_expmix ?(side = `Rhs) env mixop e (mixop', t) at =
if not (Eq.eq_mixop mixop mixop') then
error at (
"mixin notation `" ^ string_of_mixop mixop ^
"` does not match expected notation `" ^ string_of_mixop mixop' ^ "`"
);
valid_mixop env mixop;
valid_exp ~side env e t
and valid_expfield ~side env (atom1, e) (atom2, (t, _qs, _prems), _) =
Debug.(log_in_at "il.valid_expfield" e.at
(fun _ -> fmt "%s %s :%s %s %s"
(il_atom atom1) (il_exp e) (il_side side)
(il_atom atom2) (il_typ t)
)
);
if not (Eq.eq_atom atom1 atom2) then error e.at "unexpected record field";
valid_atom env atom1;
valid_exp ~side env e t
and valid_path env p t : typ =
valid_typ env t;
let t' =
match p.it with
| RootP -> t
| IdxP (p1, e1) ->
let t1 = valid_path env p1 t in
valid_exp env e1 (NumT `NatT $ e1.at);
as_list_typ "path" env Check t1 p1.at
| SliceP (p1, e1, e2) ->
let t1 = valid_path env p1 t in
valid_exp env e1 (NumT `NatT $ e1.at);
valid_exp env e2 (NumT `NatT $ e2.at);
let _ = as_list_typ "path" env Check t1 p1.at in
t1
| DotP (p1, atom) ->
let t1 = valid_path env p1 t in
valid_atom env atom;
let tfs = as_struct_typ "path" env Check t1 p1.at in
let t, _qs, _prems = find_field tfs atom p1.at in
t
in
equiv_typ env p.note t' p.at;
t'
(* Grammars *)
and valid_sym env g : typ =
Debug.(log_at "il.valid_sym" g.at (fun _ -> il_sym g) (fun t -> il_typ t)) @@ fun _ ->
match g.it with
| VarG (x, as_) ->
let ps, t, _ = Env.find_gram env x in
let s = valid_args env as_ ps Subst.empty g.at in
Subst.subst_typ s t
| NumG _ ->
(*
if n < 0x00 || n > 0xff then
error g.at "byte value out of range";
*)
NumT `NatT $ g.at
| TextG _ -> TextT $ g.at
| EpsG -> TupT [] $ g.at
| SeqG gs ->
let _ts = List.map (valid_sym env) gs in
TupT [] $ g.at
| AltG gs ->
let _ts = List.map (valid_sym env) gs in
TupT [] $ g.at
| RangeG (g1, g2) ->
let t1 = valid_sym env g1 in
let t2 = valid_sym env g2 in
equiv_typ env t1 (NumT `NatT $ g1.at) g.at;
equiv_typ env t2 (NumT `NatT $ g2.at) g.at;
NumT `NatT $ g.at
| IterG (g1, ite) ->
let it, env' = valid_iterexp ~side:`Lhs env ite g.at in
let t1 = valid_sym env' g1 in
IterT (t1, it) $ g.at
| AttrG (e, g1) ->
let t1 = valid_sym env g1 in
valid_exp ~side:`Lhs env e t1;
t1
(* Premises *)
and valid_prem env prem =
Debug.(log_in_at "il.valid_prem" prem.at (fun _ -> il_prem prem));
match prem.it with
| RulePr (x, as_, mixop, e) ->
let ps, mixop', t, _rules = Env.find_rel env x in
assert (Mixop.eq mixop mixop');
let s = valid_args env as_ ps Subst.empty prem.at in
valid_expmix env mixop e (mixop, Subst.subst_typ s t) e.at
| IfPr e ->
valid_exp env e (BoolT $ e.at)
| LetPr (e1, e2, xs) ->
let t = infer_exp env e2 in
valid_exp ~side:`Lhs env e1 t;
valid_exp env e2 t;
let target_ids = Free.{empty with varid = Set.of_list xs} in
let free_ids = Free.(free_exp e1) in
if not (Free.subset target_ids free_ids) then
error prem.at ("target identifier(s) " ^
( Free.Set.elements (Free.diff target_ids free_ids).varid |>
List.map (fun x -> "`" ^ x ^ "`") |>
String.concat ", " ) ^
" do not occur in left-hand side expression")
| ElsePr ->
()
| IterPr (prem', ite) ->
let _it, env' = valid_iterexp env ite prem.at in
valid_prem env' prem'
(* Definitions *)
and valid_arg env a p s =
Debug.(log_at "il.valid_arg" a.at
(fun _ -> fmt "%s : %s" (il_arg a) (il_param p)) (Fun.const "ok")
) @@ fun _ ->
match a.it, (Subst.subst_param s p).it with
| ExpA e, ExpP (x, t) ->
valid_exp ~side:`Lhs env e t; Subst.add_varid s x e
| TypA t, TypP x -> valid_typ env t; Subst.add_typid s x t
| DefA x', DefP (x, ps, t) ->
let ps', t', _ = Env.find_def env x' in
if not (Eval.equiv_functyp env (ps', t') (ps, t)) then
error a.at "type mismatch in function argument";
Subst.add_defid s x x'
| GramA g, GramP (x, [], t) ->
let t' = valid_sym env g in
equiv_typ env t' t a.at;
Subst.add_gramid s x g
| GramA ({it = VarG (x', as'); _} as g), GramP (x, ps, t) ->
let ps', t', _ = Env.find_gram env x' in
if as' <> [] || not (Eval.equiv_functyp env (ps', t') (ps, t)) then
error a.at "type mismatch in grammar argument";
Subst.add_gramid s x g
| _, _ ->
error a.at ("sort mismatch for argument, expected `" ^
Print.string_of_param p ^ "`, got `" ^ Print.string_of_arg a ^ "`")
and valid_args env as_ ps s at : Subst.t =
Debug.(log_if "il.valid_args" (as_ <> [] || ps <> [])
(fun _ -> fmt "{%s} : {%s}" (il_args as_) (il_params ps)) (Fun.const "ok")
) @@ fun _ ->
match as_, ps with
| [], [] -> s
| a::_, [] -> error a.at "too many arguments"
| [], _::_ -> error at "too few arguments"
| a::as', p::ps' ->
let s' = valid_arg env a p s in
valid_args env as' ps' s' at
and valid_param env p : Env.t =
match p.it with
| ExpP (x, t) ->
valid_typ env t;
Env.bind_var env x t
| TypP x ->
Env.bind_typ env x ([], [])
| DefP (x, ps, t) ->
let env' = valid_params env ps in
valid_typ env' t;
Env.bind_def env x (ps, t, [])
| GramP (x, ps, t) ->
let env' = valid_params env ps in
valid_typ env' t;
Env.bind_gram env x (ps, t, [])
and valid_quant env q = valid_param env q
and valid_params env ps = valid_binders valid_param env ps
and valid_quants env qs = valid_binders valid_quant env qs
let valid_inst env ps inst =
Debug.(log_in "il.valid_inst" line);
Debug.(log_in_at "il.valid_inst" inst.at
(fun _ -> fmt "(%s) = ..." (il_params ps))
);
match inst.it with
| InstD (qs, as_, dt) ->
let env' = valid_quants env qs in
let _s = valid_args env' as_ ps Subst.empty inst.at in
valid_deftyp env' dt
let valid_rule env mixop t rule =
Debug.(log_in "il.valid_rule" line);
Debug.(log_in_at "il.valid_rule" rule.at
(fun _ -> fmt "%s : %s = ..." (il_mixop mixop) (il_typ t))
);
match rule.it with
| RuleD (_x, qs, mixop', e, prems) ->
let env' = valid_quants env qs in
valid_expmix ~side:`Lhs env' mixop' e (mixop, t) e.at;
List.iter (valid_prem env') prems
let valid_clause env x ps t clause =
Debug.(log_in "il.valid_clause" line);
Debug.(log_in_at "il.valid_clause" clause.at
(fun _ -> fmt "%s : (%s) -> %s" (il_id x) (il_params ps) (il_typ t))
);
match clause.it with
| DefD (qs, as_, e, prems) ->
let env' = valid_quants env qs in
let s = valid_args env' as_ ps Subst.empty clause.at in
valid_exp env' e (Subst.subst_typ s t);
List.iter (valid_prem env') prems
let valid_prod env ps t prod =
Debug.(log_in "il.valid_prod" line);
Debug.(log_in_at "il.valid_prod" prod.at
(fun _ -> fmt ": (%s) -> %s" (il_params ps) (il_typ t))
);
match prod.it with
| ProdD (qs, g, e, prems) ->
let env' = valid_quants env qs in
let _t' = valid_sym env' g in
valid_exp env' e t;
List.iter (valid_prem env') prems
let infer_def env d : Env.t =
match d.it with
| TypD (x, ps, _insts) ->
let _env' = valid_params env ps in
Env.bind_typ env x (ps, [])
| RelD (x, ps, mixop, t, rules) ->
let env' = valid_params env ps in
valid_typ env' t;
Env.bind_rel env x (ps, mixop, t, rules)
| DecD (x, ps, t, clauses) ->
let env' = valid_params env ps in
valid_typ env' t;
Env.bind_def env x (ps, t, clauses)
| GramD (x, ps, t, prods) ->
let env' = valid_params env ps in
valid_typ env' t;
Env.bind_gram env x (ps, t, prods)
| _ -> env
let rec valid_def env d : Env.t =
Debug.(log_in "il.valid_def" line);
Debug.(log_in_at "il.valid_def" d.at (fun _ -> il_def d));
match d.it with
| TypD (x, ps, insts) ->
let env' = valid_params env ps in
List.iter (valid_inst env' ps) insts;
Env.bind_typ env x (ps, insts);
| RelD (x, ps, mixop, t, rules) ->
let env' = valid_params env ps in
valid_typcase env' (mixop, (t, [], []), []);
List.iter (valid_rule env' mixop t) rules;
Env.bind_rel env x (ps, mixop, t, rules)
| DecD (x, ps, t, clauses) ->
let env' = valid_params env ps in
valid_typ env' t;
List.iter (valid_clause env' x ps t) clauses;
Env.bind_def env x (ps, t, clauses)
| GramD (x, ps, t, prods) ->
let env' = valid_params env ps in
valid_typ env' t;
List.iter (valid_prod env' ps t) prods;
Env.bind_gram env x (ps, t, prods)
| RecD ds ->
let env' = valid_binders infer_def env ds in
let env' = valid_binders valid_def env' ds in
List.iter (fun d ->
match (List.hd ds).it, d.it with
| HintD _, _ | _, HintD _
| TypD _, TypD _
| RelD _, RelD _
| DecD _, DecD _
| GramD _, GramD _ -> ()
| _, _ ->
error (List.hd ds).at (" " ^ string_of_region d.at ^
": invalid recursion between definitions of different sort")
) ds;
env'
| HintD _ ->
env
(* Scripts *)
let valid ds =
ignore (valid_binders valid_def Env.empty ds)