| open Util |
| open Source |
| open Ast |
| open Xl |
| open Print |
| |
| |
| (* Errors *) |
| |
| let error at msg = Error.error at "validation" msg |
| |
| |
| (* Environment *) |
| |
| let local_env envr = ref !envr |
| |
| 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 : (exp * typ) list = |
| match expand_typ env t with |
| | TupT ets -> ets |
| | _ -> 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") |
| |
| |
| (* 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") |
| |
| |
| (* Atom Bindings *) |
| |
| 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_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) |
| ) |
| |
| |
| (* Types *) |
| |
| and valid_typ 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) |
| | BoolT |
| | NumT _ |
| | TextT -> |
| () |
| | TupT ets -> |
| List.iter (valid_typbind env) ets |
| | 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 |
| |
| and valid_typbind env (e, t) = |
| valid_typ env t; |
| valid_exp ~side:`Lhs env e t |
| |
| and valid_deftyp envr dt = |
| match dt.it with |
| | AliasT t -> |
| valid_typ !envr t |
| | StructT tfs -> |
| check_mixops "record" "field" (List.map (fun (atom, _, _) -> [[atom]]) tfs) dt.at; |
| List.iter (valid_typfield envr) tfs |
| | VariantT tcs -> |
| check_mixops "variant" "case" (List.map (fun (op, _, _) -> op) tcs) dt.at; |
| List.iter (valid_typcase envr) tcs |
| |
| and valid_typfield envr (_atom, (bs, t, prems), _hints) = |
| let envr' = local_env envr in |
| List.iter (valid_bind envr') bs; |
| valid_typ !envr' t; |
| List.iter (valid_prem !envr') prems |
| |
| and valid_typcase envr (mixop, (bs, t, prems), _hints) = |
| Debug.(log_at "il.valid_typcase" t.at |
| (fun _ -> fmt "%s %s" (il_binds bs) (il_typ t)) |
| (fun _ -> "ok") |
| ) @@ fun _ -> |
| let arity = |
| match t.it with |
| | TupT ts -> List.length ts |
| | _ -> 1 |
| in |
| if List.length mixop <> arity + 1 then |
| error t.at ("inconsistent arity in mixin notation, `" ^ string_of_mixop mixop ^ |
| "` applied to " ^ typ_string !envr t); |
| let envr' = local_env envr in |
| List.iter (valid_bind envr') bs; |
| valid_typ !envr' t; |
| List.iter (valid_prem !envr') prems |
| |
| |
| and proj_tup_typ env s e ets i : typ option = |
| match ets, i with |
| | (_eI, tI)::_, 0 -> Some tI |
| | (eI, tI)::ets', i -> |
| (match Eval.match_exp env s (ProjE (e, i) $$ e.at % tI) eI with |
| | None -> None |
| | Some s' -> proj_tup_typ env s' e ets' (i - 1) |
| | exception Eval.Irred -> None |
| ) |
| | [], _ -> assert false |
| |
| |
| (* 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 id -> Env.find_var env id |
| | 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 _binds, t, _prems = find_field tfs atom e1.at in |
| t |
| | TupE es -> |
| TupT (List.map (fun eI -> eI, infer_exp env eI) es) $ e.at |
| | CallE (id, as_) -> |
| let ps, t, _ = Env.find_def env id in |
| let s = valid_args env as_ ps Subst.empty e.at in |
| Subst.subst_typ s t |
| | IterE (e1, iterexp) -> |
| let iter, env' = valid_iterexp env iterexp e.at in |
| IterT (infer_exp env' e1, iter) $ e.at |
| | ProjE (e1, i) -> |
| let t1 = infer_exp env e1 in |
| let ets = as_tup_typ "expression" env Infer t1 e1.at in |
| if i >= List.length ets then |
| error e.at "invalid tuple projection"; |
| (match proj_tup_typ env Subst.empty e1 ets i with |
| | Some tI -> tI |
| | None -> error e.at "cannot infer type of tuple projection" |
| ) |
| | 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 _ -> |
| try |
| match e.it with |
| | VarE id when id.it = "_" && side = `Lhs -> () |
| | VarE id -> |
| let t' = Env.find_var env id 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; |
| let tfs = as_struct_typ "expression" env Check t1 e1.at in |
| let _binds, t', _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 ets = as_tup_typ "tuple" env Check t e.at in |
| if List.length es <> List.length ets then |
| error e.at ("arity mismatch for tuple, expected " ^ |
| string_of_int (List.length ets) ^ ", got " ^ string_of_int (List.length es)); |
| if not (valid_tup_exp ~side env Subst.empty es ets) then |
| as_error e.at "tuple" Check t "" |
| | CallE (id, as_) -> |
| let ps, t', _ = Env.find_def env id 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, iterexp) -> |
| let iter, env' = valid_iterexp ~side env iterexp e.at in |
| let t1 = as_iter_typ iter "iteration" env Check t e.at in |
| valid_exp ~side env' e1 t1 |
| | ProjE (e1, i) -> |
| let t1 = infer_exp env e1 in |
| let ets = as_tup_typ "expression" env Infer t1 e1.at in |
| if i >= List.length ets then |
| error e.at "invalid tuple projection"; |
| let side' = if List.length ets > 1 then `Rhs else side in |
| valid_exp ~side:side' env e1 t1; |
| (match proj_tup_typ env Subst.empty e1 ets i with |
| | Some tI -> equiv_typ env tI t e.at |
| | None -> error e.at "invalid tuple projection, cannot match pattern" |
| ) |
| | UncaseE (e1, op) -> |
| let t1 = infer_exp env e1 in |
| valid_exp ~side env e1 t1; |
| (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 _binds, t1, _prems = find_case cases op e1.at in |
| 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 |
| with exn -> |
| let bt = Printexc.get_raw_backtrace () in |
| Printf.eprintf "[valid_exp] %s\n%!" (Debug.il_exp e); |
| Printexc.raise_with_backtrace exn bt |
| |
| |
| 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_exp ~side env e t |
| |
| and valid_tup_exp ?(side = `Rhs) env s es ets = |
| Debug.(log_in "il.valid_tup_exp" |
| (fun _ -> fmt "(%s) :%s (%s)[%s]" (list il_exp es) (il_side side) (list il_typ (List.map snd ets)) (il_subst s)) |
| ); |
| match es, ets with |
| | e1::es', (e2, t)::ets' -> |
| valid_exp ~side env e1 (Subst.subst_typ s t); |
| (match Eval.match_exp env s e1 e2 with |
| | Some s' -> valid_tup_exp ~side env s' es' ets' |
| | None -> false |
| | exception Eval.Irred -> false |
| ) |
| | _, _ -> true |
| |
| and valid_expfield ~side env (atom1, e) (atom2, (_binds, t, _prems), _) = |
| if not (Eq.eq_atom atom1 atom2) then error e.at "unexpected record field"; |
| valid_exp ~side env e t |
| |
| and valid_path env p t : typ = |
| 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 |
| let tfs = as_struct_typ "path" env Check t1 p1.at in |
| let _bs, t, _prems = find_field tfs atom p1.at in |
| t |
| in |
| equiv_typ env p.note t' p.at; |
| t' |
| |
| and valid_iterexp ?(side = `Rhs) env (iter, xes) at : iter * Env.t = |
| let env' = valid_iter ~side env iter in |
| if xes = [] && iter <= List1 && side = `Rhs then error at "empty iteration"; |
| let iter' = match iter with Opt -> Opt | _ -> List in |
| iter', |
| List.fold_left (fun env' (id, e) -> |
| let t = infer_exp env e in |
| valid_exp ~side env e t; |
| let t1 = as_iter_typ iter' "iterator" env Check t e.at in |
| Env.bind_var env' id t1 |
| ) env' xes |
| |
| |
| (* 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 (id, as_) -> |
| let ps, t, _ = Env.find_gram env id 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, iterexp) -> |
| let iter, env' = valid_iterexp ~side:`Lhs env iterexp g.at in |
| let t1 = valid_sym env' g1 in |
| IterT (t1, iter) $ 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 (id, mixop, e) -> |
| let mixop', t, _rules = Env.find_rel env id in |
| assert (Mixop.eq mixop mixop'); |
| valid_expmix env mixop e (mixop, t) e.at |
| | IfPr e -> |
| valid_exp env e (BoolT $ e.at) |
| | LetPr (e1, e2, ids) -> |
| 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 ids} 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 id -> "`" ^ id ^ "`") |> |
| String.concat ", " ) ^ |
| " do not occur in left-hand side expression") |
| | ElsePr -> |
| () |
| | IterPr (prem', iterexp) -> |
| let _iter, env' = valid_iterexp env iterexp 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 (id, t) -> valid_exp ~side:`Lhs env e t; Subst.add_varid s id e |
| | TypA t, TypP id -> valid_typ env t; Subst.add_typid s id t |
| | DefA id', DefP (id, ps, t) -> |
| let ps', t', _ = Env.find_def env id' in |
| if not (Eval.equiv_functyp env (ps', t') (ps, t)) then |
| error a.at "type mismatch in function argument"; |
| Subst.add_defid s id id' |
| | GramA g, GramP (id, t) -> |
| let t' = valid_sym env g in |
| if not (Eval.equiv_typ env t' t) then |
| error a.at "type mismatch in grammar argument"; |
| Subst.add_gramid s id 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_bind envr b = |
| match b.it with |
| | ExpB (id, t) -> |
| valid_typ !envr t; |
| envr := Env.bind_var !envr id t |
| | TypB id -> |
| envr := Env.bind_typ !envr id ([], []) |
| | DefB (id, ps, t) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| valid_typ !envr' t; |
| envr := Env.bind_def !envr id (ps, t, []) |
| | GramB (id, ps, t) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| valid_typ !envr' t; |
| envr := Env.bind_gram !envr id (ps, t, []) |
| |
| and valid_param envr p = |
| match p.it with |
| | ExpP (id, t) -> |
| valid_typ !envr t; |
| envr := Env.bind_var !envr id t |
| | TypP id -> |
| envr := Env.bind_typ !envr id ([], []) |
| | DefP (id, ps, t) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| valid_typ !envr' t; |
| envr := Env.bind_def !envr id (ps, t, []) |
| | GramP (id, t) -> |
| valid_typ !envr t; |
| envr := Env.bind_gram !envr id ([], t, []) |
| |
| let valid_inst envr 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 (bs, as_, dt) -> |
| let envr' = local_env envr in |
| List.iter (valid_bind envr') bs; |
| let _s = valid_args !envr' as_ ps Subst.empty inst.at in |
| valid_deftyp envr' dt |
| |
| let valid_rule envr 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 (_id, bs, mixop', e, prems) -> |
| let envr' = local_env envr in |
| List.iter (valid_bind envr') bs; |
| valid_expmix ~side:`Lhs !envr' mixop' e (mixop, t) e.at; |
| List.iter (valid_prem !envr') prems |
| |
| let valid_clause envr ps t clause = |
| Debug.(log_in "il.valid_clause" line); |
| Debug.(log_in_at "il.valid_clause" clause.at |
| (fun _ -> fmt ": (%s) -> %s" (il_params ps) (il_typ t)) |
| ); |
| match clause.it with |
| | DefD (bs, as_, e, prems) -> |
| let envr' = local_env envr in |
| List.iter (valid_bind envr') bs; |
| let s = valid_args !envr' as_ ps Subst.empty clause.at in |
| valid_exp !envr' e (Subst.subst_typ s t); |
| List.iter (valid_prem !envr') prems |
| |
| let valid_prod envr 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 (bs, g, e, prems) -> |
| let envr' = local_env envr in |
| List.iter (valid_bind envr') bs; |
| let _t' = valid_sym !envr' g in |
| valid_exp !envr' e t; |
| List.iter (valid_prem !envr') prems |
| |
| let infer_def envr d = |
| match d.it with |
| | TypD (id, ps, _insts) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| envr := Env.bind_typ !envr id (ps, []) |
| | RelD (id, mixop, t, rules) -> |
| valid_typcase envr (mixop, ([], t, []), []); |
| envr := Env.bind_rel !envr id (mixop, t, rules) |
| | DecD (id, ps, t, clauses) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| valid_typ !envr' t; |
| envr := Env.bind_def !envr id (ps, t, clauses) |
| | GramD (id, ps, t, prods) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| valid_typ !envr' t; |
| envr := Env.bind_gram !envr id (ps, t, prods) |
| | _ -> () |
| |
| |
| let rec valid_def envr d = |
| 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 (id, ps, insts) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| List.iter (valid_inst envr ps) insts; |
| envr := Env.bind_typ !envr id (ps, insts); |
| | RelD (id, mixop, t, rules) -> |
| valid_typcase envr (mixop, ([], t, []), []); |
| List.iter (valid_rule envr mixop t) rules; |
| envr := Env.bind_rel !envr id (mixop, t, rules) |
| | DecD (id, ps, t, clauses) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| valid_typ !envr' t; |
| List.iter (valid_clause envr ps t) clauses; |
| envr := Env.bind_def !envr id (ps, t, clauses) |
| | GramD (id, ps, t, prods) -> |
| let envr' = local_env envr in |
| List.iter (valid_param envr') ps; |
| valid_typ !envr' t; |
| List.iter (valid_prod envr' ps t) prods; |
| envr := Env.bind_gram !envr id (ps, t, prods) |
| | RecD ds -> |
| List.iter (infer_def envr) ds; |
| List.iter (valid_def envr) ds; |
| 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 |
| | HintD _ -> |
| () |
| |
| |
| (* Scripts *) |
| |
| let valid ds = |
| let envr = ref Env.empty in |
| List.iter (valid_def envr) ds |