mirror of
https://github.com/c-cube/sidekick.git
synced 2025-12-07 19:55:41 -05:00
525 lines
17 KiB
OCaml
525 lines
17 KiB
OCaml
(* This file is free software. See file "license" for more details. *)
|
|
|
|
(** {1 Preprocessing AST} *)
|
|
|
|
open Sidekick_base_term
|
|
module Loc = Smtlib_utils.V_2_6.Loc
|
|
module Fmt = CCFormat
|
|
module Log = Msat.Log
|
|
|
|
module PA = Smtlib_utils.V_2_6.Ast
|
|
module BT = Sidekick_base_term
|
|
module Ty = BT.Ty
|
|
module T = BT.Term
|
|
module Fun = BT.Fun
|
|
module Stmt = BT.Statement
|
|
|
|
type 'a or_error = ('a, string) CCResult.t
|
|
|
|
let pp_loc_opt = Loc.pp_opt
|
|
|
|
(** {2 Parsing} *)
|
|
|
|
module StrTbl = CCHashtbl.Make(CCString)
|
|
|
|
module Ctx = struct
|
|
type kind =
|
|
| K_ty of ty_kind
|
|
| K_fun of Fun.t
|
|
|
|
and ty_kind =
|
|
| K_atomic of Ty.def
|
|
| K_bool
|
|
|
|
type t = {
|
|
tst: T.state;
|
|
names: (ID.t * kind) StrTbl.t;
|
|
lets: T.t StrTbl.t;
|
|
mutable loc: Loc.t option; (* current loc *)
|
|
}
|
|
|
|
let create (tst:T.state) : t = {
|
|
tst;
|
|
names=StrTbl.create 64;
|
|
lets=StrTbl.create 16;
|
|
loc=None;
|
|
}
|
|
|
|
let loc t = t.loc
|
|
let set_loc ?loc t = t.loc <- loc
|
|
|
|
let add_id_ self (s:string) (id:ID.t) (k:kind) : unit =
|
|
StrTbl.add self.names s (id,k);
|
|
()
|
|
|
|
(* locally bind [bs] and call [f], then remove bindings *)
|
|
let with_lets (self:t) (bs:(string*T.t) list) (f:unit -> 'a): 'a =
|
|
List.iter (fun (v,u) -> StrTbl.add self.lets v u) bs;
|
|
CCFun.finally ~f
|
|
~h:(fun () ->
|
|
List.iter (fun (v,_) -> StrTbl.remove self.lets v) bs)
|
|
|
|
let find_ty_def self (s:string) : Ty.def =
|
|
match StrTbl.get self.names s with
|
|
| Some (_, K_ty (K_atomic def)) -> def
|
|
| _ -> Error.errorf "expected %s to be an atomic type" s
|
|
|
|
let pp_kind out = function
|
|
| K_ty _ -> Format.fprintf out "type"
|
|
| K_fun f -> Fun.pp out f
|
|
end
|
|
|
|
let error_loc ctx : string = Fmt.sprintf "at %a: " pp_loc_opt (Ctx.loc ctx)
|
|
let errorf_ctx ctx msg =
|
|
Error.errorf ("at %a:@ " ^^ msg) pp_loc_opt (Ctx.loc ctx)
|
|
|
|
let ill_typed ctx fmt =
|
|
errorf_ctx ctx ("ill-typed: " ^^ fmt)
|
|
|
|
let check_bool_ ctx t =
|
|
if not (Ty.equal (T.ty t) Ty.bool) then (
|
|
ill_typed ctx "expected bool, got `@[%a : %a@]`" T.pp t Ty.pp (T.ty t)
|
|
)
|
|
|
|
let find_id_ ctx (s:string): ID.t * Ctx.kind =
|
|
try StrTbl.find ctx.Ctx.names s
|
|
with Not_found -> errorf_ctx ctx "name `%s` not in scope" s
|
|
|
|
(* parse a type *)
|
|
let rec conv_ty ctx (t:PA.ty) : Ty.t = match t with
|
|
| PA.Ty_bool -> Ty.bool
|
|
| PA.Ty_real -> Ty.real
|
|
| PA.Ty_app ("Int",[]) ->
|
|
ill_typed ctx "cannot handle ints for now"
|
|
(* TODO: A.Ty.int , Ctx.K_ty Ctx.K_other *)
|
|
| PA.Ty_app (f, l) ->
|
|
let def = Ctx.find_ty_def ctx f in
|
|
let l = List.map (conv_ty ctx) l in
|
|
Ty.atomic def l
|
|
| PA.Ty_arrow _ ->
|
|
ill_typed ctx "cannot handle arrow types"
|
|
|
|
let is_num s =
|
|
let is_digit_or_dot = function '0' .. '9' | '.' -> true | _ -> false in
|
|
if s.[0] = '-'
|
|
then CCString.for_all is_digit_or_dot (String.sub s 1 (String.length s-1))
|
|
else CCString.for_all is_digit_or_dot s
|
|
|
|
let string_as_q (s:string) : Q.t option =
|
|
try
|
|
let x =
|
|
try Q.of_string s
|
|
with _ ->
|
|
let f = float_of_string s in
|
|
Q.of_float f
|
|
in
|
|
Some x
|
|
with _ -> None
|
|
|
|
let t_as_q t = match Term.view t with
|
|
| T.LRA (LRA_const n) -> Some n
|
|
| _ -> None
|
|
|
|
(* conversion of terms *)
|
|
let rec conv_term (ctx:Ctx.t) (t:PA.term) : T.t =
|
|
let tst = ctx.Ctx.tst in
|
|
match t with
|
|
| PA.True -> T.true_ tst
|
|
| PA.False -> T.false_ tst
|
|
| PA.Const s when is_num s ->
|
|
let open Base_types in
|
|
begin match string_as_q s with
|
|
| Some n -> T.lra tst (LRA_const n)
|
|
| None -> errorf_ctx ctx "expected a number for %a" PA.pp_term t
|
|
end
|
|
| PA.Const f
|
|
| PA.App (f, []) ->
|
|
(* lookup in `let` table, then in type defs *)
|
|
begin match StrTbl.find ctx.Ctx.lets f with
|
|
| u -> u
|
|
| exception Not_found ->
|
|
begin match find_id_ ctx f with
|
|
| _, Ctx.K_fun f -> T.const tst f
|
|
| _, Ctx.K_ty _ ->
|
|
errorf_ctx ctx "expected term, not type; got `%s`" f
|
|
end
|
|
end
|
|
| PA.App (f, args) ->
|
|
let args = List.map (conv_term ctx) args in
|
|
begin match find_id_ ctx f with
|
|
| _, Ctx.K_fun f -> T.app_fun tst f (IArray.of_list args)
|
|
| _, Ctx.K_ty _ ->
|
|
errorf_ctx ctx "expected function, got type `%s` instead" f
|
|
end
|
|
| PA.If (a,b,c) ->
|
|
let a = conv_term ctx a in
|
|
let b = conv_term ctx b in
|
|
let c = conv_term ctx c in
|
|
Form.ite tst a b c
|
|
| PA.Fun _ | PA.Forall _ | PA.Exists _ ->
|
|
errorf_ctx ctx "cannot process lambda/quantifiers in %a" PA.pp_term t
|
|
| PA.Let (vbs, body) ->
|
|
let bs =
|
|
List.map
|
|
(fun (v,u) ->
|
|
let u = conv_term ctx u in
|
|
v, u)
|
|
vbs
|
|
in
|
|
Ctx.with_lets ctx bs (fun () -> conv_term ctx body)
|
|
| PA.Distinct l ->
|
|
let l = List.map (conv_term ctx) l in
|
|
Form.distinct_l tst l
|
|
| PA.And l ->
|
|
let l = List.map (conv_term ctx) l in
|
|
Form.and_l tst l
|
|
| PA.Or l ->
|
|
let l = List.map (conv_term ctx) l in
|
|
Form.or_l tst l
|
|
| PA.Not a ->
|
|
let a = conv_term ctx a in
|
|
Form.not_ tst a
|
|
| PA.Eq (a,b) ->
|
|
let a = conv_term ctx a in
|
|
let b = conv_term ctx b in
|
|
Form.eq tst a b
|
|
| PA.Imply (a,b) ->
|
|
let a = conv_term ctx a in
|
|
let b = conv_term ctx b in
|
|
Form.imply tst a b
|
|
| PA.Is_a (s, u) ->
|
|
let u = conv_term ctx u in
|
|
begin match find_id_ ctx s with
|
|
| _, Ctx.K_fun {Fun.fun_view=Fun_cstor c; _} ->
|
|
Term.is_a tst c u
|
|
| _ -> errorf_ctx ctx "expected `%s` to be a constructor" s
|
|
end
|
|
| PA.Match (_lhs, _l) ->
|
|
errorf_ctx ctx "TODO: support match in %a" PA.pp_term t
|
|
(* FIXME: do that properly, using [with_lets] with selectors
|
|
(* convert a regular case *)
|
|
let conv_case c vars rhs =
|
|
let c_id = find_id_ ctx c in
|
|
(* obtain the type *)
|
|
let ty_args, _ty_ret = match Ctx.find_kind ctx c_id with
|
|
| Ctx.K_cstor ty -> Ty.unfold ty
|
|
| _ ->
|
|
errorf_ctx ctx "expected `@[%a@]`@ to be a constructor" ID.pp c_id
|
|
in
|
|
(* pair variables with their type *)
|
|
if List.length vars <> List.length ty_args then (
|
|
errorf_ctx ctx
|
|
"in @[%a@] for case %a,@ wrong number of arguments (expected %d)"
|
|
PA.pp_term t ID.pp c_id (List.length ty_args);
|
|
);
|
|
let vars = List.combine vars ty_args in
|
|
Ctx.with_vars ctx vars
|
|
(fun vars ->
|
|
let rhs = conv_term ctx rhs in
|
|
c_id, vars, rhs)
|
|
in
|
|
(* convert default case, where [m] contains the partial map. We have
|
|
to complete this map *)
|
|
let lhs = conv_term ctx lhs in
|
|
let default, cases =
|
|
List.fold_left
|
|
(fun (def,cases) branch -> match branch with
|
|
| PA.Match_case (c,vars,rhs) ->
|
|
let c, vars, rhs = conv_case c vars rhs in
|
|
(* no duplicate *)
|
|
if ID.Map.mem c cases
|
|
then errorf_ctx ctx "constructor %a occurs twice" ID.pp c;
|
|
def, ID.Map.add c (vars,rhs) cases
|
|
| PA.Match_default rhs ->
|
|
(* check there is only one "default" case *)
|
|
begin match def with
|
|
| Some _ -> errorf_ctx ctx "cannot have two `default` cases"
|
|
| None ->
|
|
let rhs = conv_term ctx rhs in
|
|
Some rhs, cases
|
|
end)
|
|
(None,ID.Map.empty) l
|
|
in
|
|
(* now fill the blanks, check exhaustiveness *)
|
|
let all_cstors = Ctx.as_data ctx lhs.A.ty in
|
|
let cases = match default with
|
|
| None ->
|
|
(* check exhaustiveness *)
|
|
let missing =
|
|
all_cstors
|
|
|> List.filter (fun (cstor,_) -> not (ID.Map.mem cstor cases))
|
|
|> List.map fst
|
|
in
|
|
if missing<>[] then (
|
|
errorf_ctx ctx
|
|
"missing cases in `@[%a@]`: @[%a@]"
|
|
PA.pp_term t (Util.pp_list ID.pp) missing;
|
|
);
|
|
cases
|
|
| Some def_rhs ->
|
|
List.fold_left
|
|
(fun cases (cstor,ty_cstor) ->
|
|
if ID.Map.mem cstor cases then cases
|
|
else (
|
|
let args, _ = Ty.unfold ty_cstor in
|
|
let vars = List.mapi (fun i ty -> A.Var.makef ~ty "_%d" i) args in
|
|
ID.Map.add cstor (vars, def_rhs) cases
|
|
))
|
|
cases all_cstors
|
|
in
|
|
A.match_ lhs cases
|
|
*)
|
|
| PA.Arith (op, l) ->
|
|
let l = List.map (conv_term ctx) l in
|
|
let open Base_types in
|
|
begin match op, l with
|
|
| PA.Leq, [a;b] -> T.lra ctx.tst (LRA_pred (Leq, a, b))
|
|
| PA.Lt, [a;b] -> T.lra ctx.tst (LRA_pred (Lt, a, b))
|
|
| PA.Geq, [a;b] -> T.lra ctx.tst (LRA_pred (Geq, a, b))
|
|
| PA.Gt, [a;b] -> T.lra ctx.tst (LRA_pred (Gt, a, b))
|
|
| PA.Add, [a;b] -> T.lra ctx.tst (LRA_op (Plus, a, b))
|
|
| PA.Add, (a::l) ->
|
|
List.fold_left (fun a b -> T.lra ctx.tst (LRA_op (Plus,a,b))) a l
|
|
| PA.Minus, [a;b] -> T.lra ctx.tst (LRA_op (Minus, a, b))
|
|
| PA.Minus, (a::l) ->
|
|
List.fold_left (fun a b -> T.lra ctx.tst (LRA_op (Minus,a,b))) a l
|
|
| PA.Mult, [a;b] ->
|
|
begin match t_as_q a, t_as_q b with
|
|
| Some a, Some b -> T.lra ctx.tst (LRA_const (Q.mul a b))
|
|
| Some a, _ -> T.lra ctx.tst (LRA_mult (a, b))
|
|
| _, Some b -> T.lra ctx.tst (LRA_mult (b, a))
|
|
| None, None ->
|
|
errorf_ctx ctx "cannot handle non-linear mult %a" PA.pp_term t
|
|
end
|
|
| PA.Div, [a;b] ->
|
|
begin match t_as_q a, t_as_q b with
|
|
| Some a, Some b -> T.lra ctx.tst (LRA_const (Q.div a b))
|
|
| Some a, _ -> T.lra ctx.tst (LRA_mult (Q.inv a, b))
|
|
| _, Some b -> T.lra ctx.tst (LRA_mult (Q.inv b, a))
|
|
| None, None ->
|
|
errorf_ctx ctx "cannot handle non-linear div %a" PA.pp_term t
|
|
end
|
|
| _ ->
|
|
errorf_ctx ctx "cannot handle arith construct %a" PA.pp_term t
|
|
end;
|
|
(* FIXME: arith
|
|
let l = List.map (conv_term ctx) l in
|
|
List.iter
|
|
(fun t ->
|
|
if not (Ty.equal Ty.rat (A.ty t)) then (
|
|
errorf_ctx ctx "expected rational term,@ got `%a`" A.pp_term t
|
|
))
|
|
l;
|
|
let ty, op = match op with
|
|
| PA.Leq -> Ty.prop, A.Leq
|
|
| PA.Lt -> Ty.prop,A. Lt
|
|
| PA.Geq -> Ty.prop, A.Geq
|
|
| PA.Gt -> Ty.prop, A.Gt
|
|
| PA.Add -> Ty.rat, A.Add
|
|
| PA.Minus -> Ty.rat, A.Minus
|
|
| PA.Mult -> Ty.rat, A.Mult
|
|
| PA.Div -> Ty.rat, A.Div
|
|
in
|
|
A.arith ty op l
|
|
*)
|
|
| PA.Cast (t, ty_expect) ->
|
|
let t = conv_term ctx t in
|
|
let ty_expect = conv_ty ctx ty_expect in
|
|
if not (Ty.equal (T.ty t) ty_expect) then (
|
|
ill_typed ctx "term `%a`@ should have type `%a`" T.pp t Ty.pp ty_expect
|
|
);
|
|
t
|
|
| PA.Attr (t, [":named",_]) -> conv_term ctx t
|
|
| _ ->
|
|
errorf_ctx ctx "unsupported term %a" PA.pp_term t
|
|
|
|
let conv_fun_decl ctx f : string * Ty.t list * Ty.t =
|
|
if f.PA.fun_ty_vars <> [] then (
|
|
errorf_ctx ctx "cannot convert polymorphic function@ %a"
|
|
(PA.pp_fun_decl PA.pp_ty) f
|
|
);
|
|
let args = List.map (conv_ty ctx) f.PA.fun_args in
|
|
let ret = conv_ty ctx f.PA.fun_ret in
|
|
f.PA.fun_name, args, ret
|
|
|
|
(* FIXME: fun defs
|
|
let conv_fun_def ctx f_decl body : string * Ty.t * (unit -> T.t) =
|
|
if f_decl.PA.fun_ty_vars <> [] then (
|
|
errorf_ctx ctx "cannot convert polymorphic function@ %a"
|
|
(PA.pp_fun_decl PA.pp_typed_var) f_decl;
|
|
);
|
|
let args = conv_vars ctx f_decl.PA.fun_args in
|
|
let ty =
|
|
(List.map snd args)
|
|
(conv_ty_fst ctx f_decl.PA.fun_ret)
|
|
in
|
|
(* delayed body, for we need to declare the functions in the recursive block first *)
|
|
let conv_body() =
|
|
Ctx.with_vars ctx args
|
|
(fun args ->
|
|
A.fun_l args (conv_term ctx body))
|
|
in
|
|
f_decl.PA.fun_name, ty, conv_body
|
|
|
|
let conv_fun_defs ctx decls bodies : A.definition list =
|
|
let l = List.map2 (conv_fun_def ctx) decls bodies in
|
|
let ids = List.map (fun (f,ty,_) -> Ctx.add_id ctx f (Ctx.K_fun ty)) l in
|
|
let defs = List.map2 (fun id (_,ty,body) -> id, ty, body()) ids l in
|
|
(* parse id,ty and declare them before parsing the function bodies *)
|
|
defs
|
|
*)
|
|
|
|
let rec conv_statement ctx (s:PA.statement): Stmt.t list =
|
|
Log.debugf 4 (fun k->k "(@[<1>statement_of_ast@ %a@])" PA.pp_stmt s);
|
|
Ctx.set_loc ctx ?loc:(PA.loc s);
|
|
conv_statement_aux ctx s
|
|
|
|
and conv_statement_aux ctx (stmt:PA.statement) : Stmt.t list =
|
|
let tst = ctx.Ctx.tst in
|
|
match PA.view stmt with
|
|
| PA.Stmt_set_logic s -> [Stmt.Stmt_set_logic s]
|
|
| PA.Stmt_set_option l -> [Stmt.Stmt_set_option l]
|
|
| PA.Stmt_set_info (a,b) -> [Stmt.Stmt_set_info (a,b)]
|
|
| PA.Stmt_exit -> [Stmt.Stmt_exit]
|
|
| PA.Stmt_decl_sort (s,n) ->
|
|
let id = ID.make s in
|
|
Ctx.add_id_ ctx s id
|
|
(Ctx.K_ty (Ctx.K_atomic (Ty.Ty_uninterpreted id)));
|
|
[Stmt.Stmt_ty_decl (id, n)]
|
|
| PA.Stmt_decl fr ->
|
|
let f, args, ret = conv_fun_decl ctx fr in
|
|
let id = ID.make f in
|
|
Ctx.add_id_ ctx f id
|
|
(Ctx.K_fun (Fun.mk_undef' id args ret));
|
|
[Stmt.Stmt_decl (id, args,ret)]
|
|
| PA.Stmt_data l ->
|
|
(* first, read and declare each datatype (it can occur in the other
|
|
datatypes' constructors) *)
|
|
(* TODO:remove
|
|
let pre_parse ((data_name,arity),cases) =
|
|
if arity <> 0 then (
|
|
errorf_ctx ctx "cannot handle polymorphic datatype %s" data_name;
|
|
);
|
|
let data_id = Ctx.add_id ctx data_name (Ctx.K_ty Ctx.K_data) in
|
|
data_id, cases
|
|
in
|
|
let l = List.map pre_parse l in
|
|
*)
|
|
let module Cstor = Base_types.Cstor in
|
|
let cstors_of_data data (cstors:PA.cstor list) : Cstor.t ID.Map.t =
|
|
let parse_case {PA.cstor_name; cstor_args; cstor_ty_vars} =
|
|
if cstor_ty_vars <> [] then (
|
|
errorf_ctx ctx "cannot handle polymorphic constructor %s" cstor_name;
|
|
);
|
|
let cstor_id = ID.make cstor_name in
|
|
(* how to translate selectors *)
|
|
let mk_selectors (cstor:Cstor.t) =
|
|
List.mapi
|
|
(fun i (name,ty) ->
|
|
let select_id = ID.make name in
|
|
let sel = {
|
|
Select.
|
|
select_id;
|
|
select_ty=lazy (conv_ty ctx ty);
|
|
select_cstor=cstor;
|
|
select_i=i;
|
|
} in
|
|
(* now declare the selector *)
|
|
Ctx.add_id_ ctx name select_id
|
|
(Ctx.K_fun (Fun.select sel));
|
|
sel)
|
|
cstor_args
|
|
in
|
|
let rec cstor = {
|
|
Cstor.
|
|
cstor_id;
|
|
cstor_is_a = ID.makef "(is _ %s)" cstor_name; (* every fun needs a name *)
|
|
cstor_args=lazy (mk_selectors cstor);
|
|
cstor_arity=0;
|
|
cstor_ty_as_data=data;
|
|
cstor_ty=data.data_as_ty;
|
|
} in
|
|
(* declare cstor *)
|
|
Ctx.add_id_ ctx cstor_name cstor_id (Ctx.K_fun (Fun.cstor cstor));
|
|
cstor_id, cstor
|
|
in
|
|
let cstors = List.map parse_case cstors in
|
|
ID.Map.of_list cstors
|
|
in
|
|
(* now parse constructors *)
|
|
let l =
|
|
List.map
|
|
(fun ((data_name,arity), (cstors:PA.cstor list)) ->
|
|
if arity <> 0 then (
|
|
(* TODO: handle poly datatypes properly *)
|
|
errorf_ctx ctx "cannot handle polymorphic datatype %s" data_name;
|
|
);
|
|
let data_id = ID.make data_name in
|
|
let rec data = {
|
|
Data.
|
|
data_id;
|
|
data_cstors=lazy (cstors_of_data data cstors);
|
|
data_as_ty=lazy (
|
|
let def = Ty.Ty_data { data; } in
|
|
Ty.atomic def []
|
|
);
|
|
} in
|
|
Ctx.add_id_ ctx data_name data_id
|
|
(Ctx.K_ty (Ctx.K_atomic (Ty.Ty_data {data})));
|
|
data)
|
|
l
|
|
in
|
|
(* now force definitions *)
|
|
List.iter
|
|
(fun {Data.data_cstors=lazy m;data_as_ty=lazy _;_} ->
|
|
ID.Map.iter (fun _ ({Cstor.cstor_args=lazy l;_} as r) -> r.cstor_arity <- List.length l) m;
|
|
())
|
|
l;
|
|
[Stmt.Stmt_data l]
|
|
| PA.Stmt_funs_rec _defs ->
|
|
errorf_ctx ctx "not implemented: definitions" PA.pp_stmt stmt
|
|
(* TODO
|
|
let {PA.fsr_decls=decls; fsr_bodies=bodies} = defs in
|
|
if List.length decls <> List.length bodies then (
|
|
errorf_ctx ctx "declarations and bodies should have same length";
|
|
);
|
|
let defs = conv_fun_defs ctx decls bodies in
|
|
[A.Define defs]
|
|
*)
|
|
| PA.Stmt_fun_def
|
|
{PA.fr_decl={PA.fun_ty_vars=[]; fun_args=[]; fun_name; fun_ret}; fr_body} ->
|
|
(* turn [def f : ret := body] into [decl f : ret; assert f=body] *)
|
|
let ret = conv_ty ctx fun_ret in
|
|
let id = ID.make fun_name in
|
|
let f = Fun.mk_undef_const id ret in
|
|
Ctx.add_id_ ctx fun_name id (Ctx.K_fun f);
|
|
let rhs = conv_term ctx fr_body in
|
|
[ Stmt.Stmt_decl (id,[],ret);
|
|
Stmt.Stmt_assert (Form.eq tst (T.const tst f) rhs);
|
|
]
|
|
| PA.Stmt_fun_rec _
|
|
| PA.Stmt_fun_def _
|
|
-> errorf_ctx ctx "unsupported definition: %a" PA.pp_stmt stmt
|
|
| PA.Stmt_assert t ->
|
|
let t = conv_term ctx t in
|
|
check_bool_ ctx t;
|
|
[Stmt.Stmt_assert t]
|
|
| PA.Stmt_check_sat -> [Stmt.Stmt_check_sat]
|
|
| PA.Stmt_check_sat_assuming _
|
|
| PA.Stmt_get_assertions
|
|
| PA.Stmt_get_option _
|
|
| PA.Stmt_get_info _
|
|
| PA.Stmt_get_model
|
|
| PA.Stmt_get_proof
|
|
| PA.Stmt_get_unsat_core
|
|
| PA.Stmt_get_unsat_assumptions
|
|
| PA.Stmt_get_assignment
|
|
| PA.Stmt_reset
|
|
| PA.Stmt_reset_assertions
|
|
| PA.Stmt_push _
|
|
| PA.Stmt_pop _
|
|
| PA.Stmt_get_value _
|
|
->
|
|
(* TODO: handle more of these *)
|
|
errorf_ctx ctx "cannot handle statement %a" PA.pp_stmt stmt
|
|
|