package smtml

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file interpret.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
(* SPDX-License-Identifier: MIT *)
(* Copyright (C) 2023-2024 formalsec *)
(* Written by the Smtml programmers *)

include Interpret_intf

module Status = struct
  let pp fmt = function
    | `Sat -> Fmt.pf fmt "sat"
    | `Unsat -> Fmt.pf fmt "unsat"
    | `Unknown -> Fmt.pf fmt "unknown"

  let is_valid expected real =
    match (expected, real) with
    | `Unknown, _ -> true
    | `Sat, `Sat | `Unsat, `Unsat -> true
    | `Sat, `Unsat | `Unsat, `Sat | (`Sat | `Unsat), `Unknown -> false

  let of_expr (t : Expr.t) : [ `Sat | `Unsat | `Unknown ] option =
    match Expr.view t with
    | App ({ name = Simple ":status"; _ }, [ st ]) -> (
      match Expr.view st with
      | Symbol { name = Simple "sat"; _ } -> Some `Sat
      | Symbol { name = Simple "unsat"; _ } -> Some `Unsat
      | Symbol { name = Simple "unknown"; _ } -> Some `Unknown
      | _ ->
        Log.debug (fun k -> k "Unrecognised status value: %a" Expr.pp st);
        None )
    | _ -> None
end

module Make (Solver : Solver_intf.S) = struct
  open Ast

  type solver = Solver.t

  type exec_state = solver state

  let init_state stmts expected_status =
    let params = Params.(default () $ (Model, true)) in
    let solver = Solver.create ~params () in
    Solver.push solver;
    { stmts; smap = Hashtbl.create 16; solver; expected_status }

  let eval stmt (state : exec_state) ~no_strict_status ~quiet : exec_state =
    let { solver; expected_status; _ } = state in
    match stmt with
    | Assert e ->
      Log.debug (fun k -> k "assert: %a" Expr.pp e);
      Solver.add solver [ e ];
      state
    | Check_sat assumptions -> begin
      Log.debug (fun k -> k "check-sat: %a" Expr.pp_list assumptions);
      let status = Solver.check solver assumptions in
      if not quiet then Fmt.pr "%a@." Status.pp status;
      begin match expected_status with
      | None -> ()
      | Some expected_status ->
        if not (Status.is_valid expected_status status) then
          if no_strict_status then
            Log.err (fun k ->
              k "Expected status: %a, but solver returned %a" Status.pp
                expected_status Status.pp status )
          else
            Fmt.failwith "Expected status: %a, but solver returned %a" Status.pp
              expected_status Status.pp status
      end;
      state
    end
    | Declare_const _x -> state
    | Declare_fun _x -> state
    | Echo x ->
      if not quiet then Fmt.pr "%a" Fmt.string x;
      state
    | Exit -> { state with stmts = [] }
    | Get_model ->
      assert (
        match Solver.check solver [] with
        | `Sat -> true
        | `Unsat | `Unknown -> false );
      let model = Solver.model solver in
      if not quiet then
        Fmt.pr "%a@." (Fmt.option (Model.pp ~no_values:false)) model;
      state
    | Push _n ->
      Solver.push solver;
      state
    | Pop n ->
      Solver.pop solver n;
      state
    | Set_logic _logic ->
      state
      (* FIXME: Ignoring logic for now *)
      (* let solver = Solver.create ~logic () in *)
      (* Solver.push solver; *)
      (* { state with solver } *)
    | Set_info attr ->
      Log.debug (fun k -> k "Unsupported: (set-info %a)" Expr.pp attr);
      state
    | Get_assertions | Get_assignment | Reset | Reset_assertions | Get_info _
    | Get_option _ | Get_value _ | Set_option _ ->
      Log.debug (fun k -> k "Unsupported: %a" Ast.pp stmt);
      state

  let rec loop (state : exec_state) ~no_strict_status ~quiet : exec_state =
    match state.stmts with
    | [] -> state
    | stmt :: stmts ->
      loop
        (eval stmt { state with stmts } ~no_strict_status ~quiet)
        ~no_strict_status ~quiet

  let find_expected_status (script : Ast.Script.t) :
    [ `Sat | `Unsat | `Unknown ] option =
    List.find_map
      (fun cmd ->
        match cmd with Ast.Set_info expr -> Status.of_expr expr | _ -> None )
      script

  let start ?state ?(quiet = false) ~no_strict_status (stmts : Ast.Script.t) :
    exec_state =
    Log.debug (fun k -> k "Starting interpreter...");
    let expected_status = find_expected_status stmts in
    let st =
      match state with
      | None -> init_state stmts expected_status
      | Some st ->
        Solver.pop st.solver 1;
        Solver.push st.solver;
        { st with stmts; expected_status }
    in
    loop st ~no_strict_status ~quiet
end