langkit_support_24.0.0_e7945e68/langkit_support-adalog-generic_main_support.adb

  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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
--
--  Copyright (C) 2014-2022, AdaCore
--  SPDX-License-Identifier: Apache-2.0
--

with Ada.Command_Line;
with Ada.Environment_Variables;
with Ada.Exceptions; use Ada.Exceptions;
with Ada.Text_IO;    use Ada.Text_IO;
with Ada.Unchecked_Deallocation;

with Langkit_Support.Images;
with Langkit_Support.Vectors;

package body Langkit_Support.Adalog.Generic_Main_Support is

   procedure Free is new Ada.Unchecked_Deallocation
     (Logic_Var_Record, Refs.Logic_Var);

   --  The following booleans determine which solver configurations to run in
   --  tests. The ``Setup_Traces`` procedure initializes them with default
   --  values, possibly modified depending on environment variables, for
   --  convenience when debugging tests.

   Run_Sym_Without_Opts : Boolean;
   Run_Sym_With_Opts    : Boolean;

   ------------
   -- Create --
   ------------

   function Create (Name : String) return Refs.Logic_Var is
   begin
      return R : constant Refs.Logic_Var := new Logic_Var_Record do
         Reset (R);
         R.Dbg_Name := new String'(Name);
         Variables.Append (R);
      end return;
   end Create;

   ---------
   -- "+" --
   ---------

   function "+" (R : Relation) return Relation is
   begin
      Relations.Append (R);
      return R;
   end "+";

   ---------------
   -- Solve_All --
   ---------------

   procedure Solve_All (Rel : Relation; Timeout : Natural := 0) is

      type Var_And_Val is record
         Var     : Refs.Logic_Var;
         Defined : Boolean;
         Val     : T;
      end record;
      --  We want to keep track of the various solutions found. Since solution
      --  values are stored in variables, this means that we need to save the
      --  values when a solution is found, as the next solution will override
      --  them in variables.

      function Image (Self : Var_And_Val) return String
      is
        (Refs.Image (Self.Var) & " = "
         & (if Self.Defined
            then Image (Self.Val)
            else "<undefined>"));

      type Solution is array (Positive range <>) of Var_And_Val;
      type Solution_Access is access all Solution;
      procedure Free is new Ada.Unchecked_Deallocation
        (Solution, Solution_Access);

      package Solution_Vectors is new Langkit_Support.Vectors
        (Solution_Access);
      procedure Free (Self : in out Solution_Vectors.Vector);
      --  Free all solutions in Self and destroy the vector

      function Equivalent
        (Left, Right : Solution_Vectors.Vector) return Boolean;
      --  Return whether the two vectors of solutions are equal

      Solutions              : Solution_Vectors.Vector :=
        Solution_Vectors.Empty_Vector;
      Solutions_Without_Opts : Solution_Vectors.Vector :=
        Solution_Vectors.Empty_Vector;

      function Solution_Callback (Vars : Logic_Var_Array) return Boolean;
      --  Callback for ``Solve``. Print the given solution and append it to
      --  ``Solutions``, then return True to continue looking for other
      --  solutions.

      function Image is new Langkit_Support.Images.Array_Image
        (Var_And_Val, Positive, Solution);

      procedure Run_Solve (Opts : Solve_Options_Type);
      --  Wrapper around the ``Solve`` procedure, to catch and display uncaught
      --  exceptions.

      ----------
      -- Free --
      ----------

      procedure Free (Self : in out Solution_Vectors.Vector) is
         S_Mut : Solution_Access;
      begin
         for S of Self loop
            S_Mut := S;
            Free (S_Mut);
         end loop;
         Self.Destroy;
      end Free;

      ----------------
      -- Equivalent --
      ----------------

      function Equivalent
        (Left, Right : Solution_Vectors.Vector) return Boolean
      is
      begin
         if Left.Length /= Right.Length then
            return False;
         end if;

         for I in 1 .. Left.Length loop
            declare
               S_L : Solution renames Left.Get (I).all;
               S_R : Solution renames Right.Get (I).all;
            begin
               if S_L'Length /= S_R'Length then
                  return False;
               end if;

               for J in S_L'Range loop
                  declare
                     L : Var_And_Val renames S_L (J);
                     R : Var_And_Val renames S_R (J);
                  begin
                     if Refs."/=" (L.Var, R.Var)
                        or else L.Defined /= R.Defined
                        or else (L.Defined and then L.Val /= R.Val)
                     then
                        return False;
                     end if;
                  end;
               end loop;
            end;
         end loop;

         return True;
      end Equivalent;

      -----------------------
      -- Solution_Callback --
      -----------------------

      function Solution_Callback (Vars : Logic_Var_Array) return Boolean is
         S : constant Solution_Access := new Solution (Vars'Range);
      begin
         for I in Vars'Range loop
            declare
               V : Refs.Logic_Var renames Vars (I);
            begin
               S (I) := (Var     => V,
                         Defined => Refs.Is_Defined (V),
                         Val     => Refs.Get_Value (V));
            end;
         end loop;
         Solutions.Append (S);

         Put_Line ("Solution: " & Image (S.all));
         return True;
      end Solution_Callback;

      ---------------
      -- Run_Solve --
      ---------------

      procedure Run_Solve (Opts : Solve_Options_Type) is
      begin
         Solve (Rel, Solution_Callback'Access, Opts, Timeout);
      exception
         when Early_Binding_Error =>
            Put_Line ("Resolution failed with Early_Binding_Error");
         when Timeout_Error =>
            Put_Line ("Resolution failed with Timeout_Error");
         when Exc : others =>
            Put_Line ("  -> " & Exception_Name (Exc)
                      & ": " & Exception_Message (Exc));
      end Run_Solve;

   begin
      Put_Line ("Solving relation:");
      Put_Line (Image (Rel));

      --  Solve both without and with optimizations

      if Run_Sym_Without_Opts then
         Put_Line ("... without optimizations:");
         Run_Solve ((others => False));
         New_Line;
         Solutions_Without_Opts := Solutions;
         Solutions := Solution_Vectors.Empty_Vector;
      end if;

      if Run_Sym_With_Opts then
         Put_Line ("... with all optimizations:");
         Run_Solve ((others => True));
         New_Line;
      end if;

      --  Check that we had the same results in both cases

      if Run_Sym_Without_Opts
         and then Run_Sym_With_Opts
         and then not Equivalent (Solutions_Without_Opts, Solutions)
      then
         Put_Line ("ERROR: solutions are not the same");
         New_Line;
         Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
      end if;

      --  Clean up, we are done

      Free (Solutions_Without_Opts);
      Free (Solutions);

   exception
      when others =>
         Free (Solutions_Without_Opts);
         Free (Solutions);
         raise;
   end Solve_All;

   ---------
   -- "-" --
   ---------

   function "-" (S : String) return String_Access is
   begin
      return Result : constant String_Access := new String'(S) do
         Strings.Append (Result);
      end return;
   end "-";

   --------------
   -- Run_Main --
   --------------

   procedure Run_Main (Main : access procedure) is
   begin
      Setup_Traces;
      Main.all;
      Finalize;
   end Run_Main;

   ------------------
   -- Setup_Traces --
   ------------------

   procedure Setup_Traces is
      package Env renames Ada.Environment_Variables;
      Var_Name : constant String := "ADALOG_SOLVER_CFG";
      Cfg      : constant String := Env.Value (Var_Name, Default => "");
   begin
      GNATCOLL.Traces.Parse_Config_File;

      Run_Sym_Without_Opts := False;
      Run_Sym_With_Opts := False;

      if Cfg = "" then
         Run_Sym_Without_Opts := True;
         Run_Sym_With_Opts := True;
      elsif Cfg = "sym" then
         Run_Sym_Without_Opts := True;
      elsif Cfg = "sym-opts" then
         Run_Sym_With_Opts := True;
      else
         raise Program_Error
           with "Invalid value for env var """ & Var_Name & """";
      end if;
   end Setup_Traces;

   --------------
   -- Finalize --
   --------------

   procedure Finalize is
      Used : Boolean := False;
   begin
      for R of Relations loop
         Used := True;
         Dec_Ref (R);
      end loop;

      for V of Variables loop
         Refs.Destroy (V.all);
         Free (V);
      end loop;

      for S of Strings loop
         Free (S);
      end loop;

      Relations := Relation_Vectors.Empty_Vector;
      Variables := Variable_Vectors.Empty_Vector;
      Strings := String_Access_Vectors.Empty_Vector;

      if Used then
         Put_Line ("Done.");
      end if;
   end Finalize;

end Langkit_Support.Adalog.Generic_Main_Support;