adsl 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,378 @@
1
+ require 'spass/ruby_extensions'
2
+ require 'fol/first_order_logic'
3
+
4
+ module SpassTranslator
5
+ def replace_conjecture(input, conjecture)
6
+ input.gsub(/list_of_formulae\s*\(\s*conjectures\s*\)\s*\..*?end_of_list\./m, <<-SPASS)
7
+ list_of_formulae(conjectures).
8
+ formula(#{conjecture.resolve_spass}).
9
+ end_of_list.
10
+ SPASS
11
+ end
12
+
13
+ class Predicate
14
+ attr_accessor :name, :arity
15
+
16
+ include FOL
17
+
18
+ def initialize(name, arity)
19
+ @name = name
20
+ @arity = arity
21
+ end
22
+
23
+ def [](*args)
24
+ args = args.flatten
25
+ return "#{@name}(#{ (1..@arity).map{ |i| "${#{i}}"}.join(", ") })".resolve_params(*args)
26
+ end
27
+ end
28
+
29
+ class ContextCommon
30
+ attr_accessor :parent, :level
31
+
32
+ def type_pred(*args)
33
+ return @level == 0 ? 'true' : @type_pred[*args.flatten]
34
+ end
35
+
36
+ def initialize(translation, name, parent)
37
+ @level = parent.nil? ? 0 : parent.level + 1
38
+ @translation = translation
39
+ @parent = parent
40
+
41
+ unless parent.nil?
42
+ @type_pred = translation.create_predicate(name, @level)
43
+
44
+ translation.reserve_names @parent.p_names do |ps|
45
+ translation.create_formula FOL::ForAll.new(ps, :c, FOL::Implies.new(
46
+ type_pred(ps, :c), @parent.type_pred(ps)
47
+ ))
48
+ end
49
+ translation.reserve_names @parent.p_names, @parent.p_names, :c do |p1s, p2s, c|
50
+ translation.create_formula FOL::ForAll.new(p1s, p2s, :c, FOL::Implies.new(
51
+ FOL::And.new(type_pred(p1s, c), type_pred(p2s, c)),
52
+ FOL::PairwiseEqual.new(p1s, p2s)
53
+ ))
54
+ end
55
+ end
56
+ end
57
+
58
+ def same_level_before_formula(parents, c1, c2)
59
+ raise 'To be implemented'
60
+ end
61
+
62
+ def p_names(num = level)
63
+ num.times.map{ |i| "p#{i+1}".to_sym }
64
+ end
65
+
66
+ def self.get_common_context(c1, c2)
67
+ while c1.level > c2.level
68
+ c1 = c1.parent
69
+ end
70
+ while c2.level > c1.level
71
+ c2 = c2.parent
72
+ end
73
+ while c1 != c2
74
+ c1 = c1.parent
75
+ c2 = c2.parent
76
+ end
77
+ return c1
78
+ end
79
+
80
+ def before(c2, c1var, c2var, executed_before)
81
+ c1 = self
82
+ @translation.reserve_names((1..c1.level-1).map{|i| "parent_a#{i}"}) do |context1_names|
83
+ @translation.reserve_names((1..c2.level-1).map{|i| "parent_b#{i}"}) do |context2_names|
84
+ context1_names << c1var
85
+ context2_names << c2var
86
+ common_context = ContextCommon.get_common_context c1, c2
87
+ prereq_formulae = FOL::And.new(c1.type_pred(context1_names), c2.type_pred(context2_names))
88
+
89
+ solution = executed_before
90
+ parent_args = context1_names.first(common_context.level)
91
+ parent_args.pop
92
+ while common_context.parent
93
+ c1_name = context1_names[common_context.level-1]
94
+ c2_name = context2_names[common_context.level-1]
95
+ solution = FOL::And.new(
96
+ FOL::Implies.new(common_context.same_level_before_formula(parent_args, c1_name, c2_name), true),
97
+ FOL::Implies.new(common_context.same_level_before_formula(parent_args, c2_name, c1_name), false),
98
+ FOL::Implies.new(
99
+ FOL::Not.new(
100
+ common_context.same_level_before_formula(parent_args, c1_name, c2_name),
101
+ common_context.same_level_before_formula(parent_args, c2_name, c1_name)
102
+ ),
103
+ solution
104
+ )
105
+ )
106
+ common_context = common_context.parent
107
+ parent_args.pop
108
+ end
109
+ solution = FOL::Implies.new(FOL::And.new(prereq_formulae), solution)
110
+ if context1_names.length > 1 or context2_names.length > 1
111
+ solution = FOL::ForAll.new([context1_names[0..-2], context2_names[0..-2]], solution)
112
+ end
113
+ return solution
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ class FlatContext < ContextCommon
120
+ def initialize(translation, name, parent)
121
+ super
122
+ end
123
+
124
+ def same_level_before_formula(ps, c1, c2)
125
+ false
126
+ end
127
+ end
128
+
129
+ class ChainedContext < ContextCommon
130
+ attr_accessor :before_pred, :just_before, :first, :last
131
+ include FOL
132
+
133
+ def initialize(translation, name, parent)
134
+ super
135
+
136
+ @before_pred = translation.create_predicate "#{@type_pred.name}_before", @type_pred.arity + 1
137
+ @just_before = translation.create_predicate "#{@type_pred.name}_just_before", @type_pred.arity + 1
138
+ @first = translation.create_predicate "#{@type_pred.name}_first", @type_pred.arity
139
+ @last = translation.create_predicate "#{@type_pred.name}_last", @type_pred.arity
140
+
141
+ ps = []
142
+ (@type_pred.arity-1).times{ |i| ps << "p#{i}" }
143
+ translation.create_formula _for_all(ps, :c, _not(@before_pred[ps, :c, :c]))
144
+ translation.create_formula _for_all(ps, :c1, :c2, _implies(@before_pred[ps, :c1, :c2], _and(
145
+ @type_pred[ps, :c1],
146
+ @type_pred[ps, :c2],
147
+ _not(@before_pred[ps, :c2, :c1]),
148
+ _implies(
149
+ _and(@type_pred[ps, :c1], @type_pred[ps, :c2]),
150
+ _or(_equal(:c1, :c2), @before_pred[ps, :c1, :c2], @before_pred[ps, :c2, :c1])
151
+ )
152
+ )))
153
+ translation.create_formula _for_all(ps, :c1, :c2, :c3, _implies(
154
+ _and(@before_pred[ps, :c1, :c2], @before_pred[ps, :c2, :c3]),
155
+ @before_pred[ps, :c1, :c3]
156
+ ))
157
+ translation.create_formula _for_all(ps, :c1, :c2, _equiv(
158
+ @just_before[ps, :c1, :c2],
159
+ _and(
160
+ @before_pred[ps, :c1, :c2],
161
+ _not(_exists(:mid, _and(@before_pred[ps, :c1, :mid], @before_pred[ps, :mid, :c2])))
162
+ )
163
+ ))
164
+ translation.create_formula _for_all(ps, _and(
165
+ _equiv(
166
+ _exists(:c, @type_pred[ps, :c]),
167
+ _exists(:c, @first[ps, :c]),
168
+ _exists(:c, @last[ps, :c])
169
+ ),
170
+ _for_all(ps, :c, _implies(
171
+ @type_pred[ps, :c],
172
+ _one_of(@last[ps, :c], _exists(:next, @just_before[ps, :c, :next]))
173
+ )),
174
+ _for_all(ps, :c, _equiv(@first[ps, :c],
175
+ _and(@type_pred[ps, :c], _not(_exists(:pre, @before_pred[ps, :pre, :c])))
176
+ )),
177
+ _for_all(ps, :c, _equiv(@last[ps, :c],
178
+ _and(@type_pred[ps, :c], _not(_exists(:post, @before_pred[ps, :c, :post])))
179
+ ))
180
+ ))
181
+ end
182
+
183
+ def same_level_before_formula(ps, c1, c2)
184
+ @before_pred[ps, c1, c2]
185
+ end
186
+ end
187
+
188
+ class Translation
189
+ attr_accessor :context, :prev_state, :invariant_state
190
+ attr_reader :existed_initially, :exists_finally, :root_context
191
+ attr_reader :is_object, :is_tuple, :is_either_resolution, :resolved_as_true
192
+ attr_reader :create_obj_stmts, :delete_obj_stmts, :all_contexts, :classes
193
+ attr_reader :conjectures
194
+
195
+ include FOL
196
+
197
+ def initialize
198
+ @classes = []
199
+ @temp_vars = []
200
+ @functions = []
201
+ @predicates = []
202
+ @formulae = [[]]
203
+ @conjectures = []
204
+ @all_contexts = []
205
+ @existed_initially = create_predicate :existed_initially, 1
206
+ @exists_finally = create_predicate :exists_finally, 1
207
+ @is_object = create_predicate :is_object, 1
208
+ @is_tuple = create_predicate :is_tuple, 1
209
+ @is_either_resolution = create_predicate :is_either_resolution, 1
210
+ @root_context = create_context 'root_context', true, nil
211
+ @context = @root_context
212
+ # {class => [[before_stmt, context], [after_stmt, context]]}
213
+ @create_obj_stmts = Hash.new{ |hash, klass| hash[klass] = [] }
214
+ @delete_obj_stmts = Hash.new{ |hash, klass| hash[klass] = [] }
215
+ @prev_state = create_state :init_state
216
+
217
+ @invariant_state = nil
218
+ end
219
+
220
+ def create_state name
221
+ state = create_predicate name, @context.level + 1
222
+ reserve_names([:c_1] * @context.level, :o) do |cs, o|
223
+ create_formula FOL::ForAll.new(cs, o, FOL::Implies.new(
224
+ state[cs, o],
225
+ FOL::And.new(@context.type_pred(cs), FOL::Or.new(@is_object[o], @is_tuple[o]))
226
+ ))
227
+ end
228
+ state
229
+ end
230
+
231
+ def create_context(name, flat, parent)
232
+ context = nil
233
+ if flat
234
+ context = FlatContext.new self, name, parent
235
+ else
236
+ context = ChainedContext.new self, name, parent
237
+ end
238
+ @all_contexts << context
239
+ context
240
+ end
241
+
242
+ def push_formula_frame
243
+ @formulae.push []
244
+ end
245
+
246
+ def pop_formula_frame
247
+ @formulae.pop
248
+ end
249
+
250
+ def create_formula(formula)
251
+ raise ArgumentError, 'Formula not resolveable to Spass' unless formula.class.method_defined? :resolve_spass
252
+ @formulae.last.push formula
253
+ end
254
+
255
+ def create_conjecture(formula)
256
+ raise ArgumentError, 'Formula not resolveable to Spass' unless formula.class.method_defined? :resolve_spass
257
+ @conjectures.push formula
258
+ end
259
+
260
+ def create_function(name, arity)
261
+ function = Predicate.new get_pred_name(name.to_s), arity
262
+ @functions << function
263
+ function
264
+ end
265
+
266
+ def create_predicate(name, arity)
267
+ pred = Predicate.new get_pred_name(name.to_s), arity
268
+ @predicates << pred
269
+ pred
270
+ end
271
+
272
+ def get_pred_name prefix
273
+ prefix_of_prefix = prefix
274
+ prefix_of_prefix = prefix.scan(/^(.+)_\d+$/).first.first if prefix =~ /^.+_\d+$/
275
+
276
+ already_existing = (@functions + @predicates).select{ |a| a.name =~ /^#{Regexp.escape prefix_of_prefix}(_\d+)?$/ }.max_by{ |a| a.name }
277
+ return prefix if already_existing.nil?
278
+ return already_existing.name.increment_suffix
279
+ end
280
+
281
+ def _reserve_names(*names)
282
+ result = []
283
+ names.each do |name|
284
+ if name.is_a? Array
285
+ result << _reserve_names(*name)
286
+ else
287
+ while @temp_vars.include? name
288
+ name = name.to_s.increment_suffix.to_sym
289
+ end
290
+ @temp_vars.push name
291
+ result << name
292
+ end
293
+ end
294
+ result
295
+ end
296
+
297
+ def reserve_names(*names)
298
+ result = _reserve_names(*names)
299
+ yield *result
300
+ ensure
301
+ names.flatten.length.times do
302
+ @temp_vars.pop
303
+ end
304
+ end
305
+
306
+ def gen_formula_for_unique_arg(pred, *args)
307
+ individuals = []
308
+ args.each do |arg|
309
+ arg = arg.is_a?(Range) ? arg.to_a : [arg].flatten
310
+ next if arg.empty?
311
+ vars1 = (1..pred.arity).map{ |i| "e#{i}" }
312
+ vars2 = vars1.dup
313
+ as = []
314
+ bs = []
315
+ arg.each do |index|
316
+ a = "a#{index+1}".to_sym
317
+ vars1[index] = a
318
+ b = "b#{index+1}".to_sym
319
+ vars2[index] = b
320
+ as << a
321
+ bs << b
322
+ end
323
+ reserve_names (vars1 | vars2) do
324
+ individuals << _for_all(vars1 | vars2, _implies(_and(pred[vars1], pred[vars2]), _pairwise_equal(as, bs)))
325
+ end
326
+ end
327
+ return true if individuals.empty?
328
+ formula = _and(individuals)
329
+ create_formula formula
330
+ return formula
331
+ end
332
+
333
+ def spass_wrap(with, what)
334
+ return "" if what.length == 0
335
+ return with % what
336
+ end
337
+
338
+ def spass_list_of(what, *content)
339
+ spass_wrap "list_of_#{what.to_s}.%s\nend_of_list.", content.flatten.map{ |c| "\n " + c.to_s }.join("")
340
+ end
341
+
342
+ def to_spass_string
343
+ functions = @functions.map{ |f| "(#{f.name}, #{f.arity})" }.join(", ")
344
+ predicates = @predicates.map{ |p| "(#{p.name}, #{p.arity})" }.join(", ")
345
+ formulae = @formulae.first.map do |f|
346
+ begin
347
+ next "formula(#{f.resolve_spass})."
348
+ rescue => e
349
+ pp f
350
+ raise e
351
+ end
352
+ end
353
+ conjectures = @conjectures.map{ |f| "formula(#{f.resolve_spass})." }
354
+ <<-SPASS
355
+ begin_problem(Blahblah).
356
+ list_of_descriptions.
357
+ name({* *}).
358
+ author({* *}).
359
+ status(satisfiable).
360
+ description( {* *} ).
361
+ end_of_list.
362
+ #{spass_list_of( :symbols,
363
+ spass_wrap("functions[%s].", functions),
364
+ spass_wrap("predicates[%s].", predicates)
365
+ )}
366
+ #{spass_list_of( "formulae(axioms)",
367
+ formulae
368
+ )}
369
+ #{spass_list_of( "formulae(conjectures)",
370
+ conjectures
371
+ )}
372
+ end_problem.
373
+ SPASS
374
+ end
375
+
376
+ end
377
+ end
378
+
data/lib/spass/util.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Spass
2
+ module Util
3
+ def replace_conjecture(input, conjecture)
4
+ input.gsub(/list_of_formulae\s*\(\s*conjectures\s*\)\s*\..*?end_of_list\./m, <<-SPASS)
5
+ list_of_formulae(conjectures).
6
+ formula(#{conjecture.resolve_spass}).
7
+ end_of_list.
8
+ SPASS
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ # A writer into CSV format that takes lines in hash format
2
+ # row = {:column1 => value1, :column2 => value2, ... }
3
+ # All rows are buffered together and a csv file is output with
4
+ # the union of individual column sets
5
+ # if the order of columns matter, supply an OrderedHash
6
+ # instance for each row
7
+
8
+ require 'set'
9
+
10
+ module Util
11
+ class CSVHashFormatter
12
+ def escape(obj)
13
+ "\"#{obj.to_s.gsub('"', '""')}\""
14
+ end
15
+
16
+ def initialize
17
+ @row_hashes = []
18
+ @columns = []
19
+ end
20
+
21
+ def add_row(row)
22
+ @row_hashes << row
23
+ row.keys.each do |key|
24
+ @columns << key unless @columns.include? key
25
+ end
26
+ end
27
+
28
+ alias_method :<<, :add_row
29
+
30
+ def to_s
31
+ return '' if @columns.empty?
32
+ output = @columns.map{ |c| escape(c) }.join(',') + "\n"
33
+ @row_hashes.each do |row|
34
+ output = output + @columns.map{ |c| escape(row[c] || '') }.join(',') + "\n"
35
+ end
36
+ output
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ require 'test/unit'
2
+ require 'parser/adsl_parser.tab'
3
+ require 'spass/bin'
4
+ require 'spass/spass_ds_extensions'
5
+ require 'spass/util'
6
+
7
+ class Test::Unit::TestCase
8
+ include Spass::Bin
9
+ include Spass::Util
10
+
11
+ SPASS_TIMEOUT = 5
12
+
13
+ def adsl_assert(expected_result, input, options={})
14
+ ds_spec = ADSL::ADSLParser.new.parse input
15
+ raise "Exactly one action required in ADSL" if ds_spec.actions.length != 1
16
+ action_name = ds_spec.actions.first.name
17
+ spass = ds_spec.translate_action(action_name)
18
+ spass = replace_conjecture spass, options[:conjecture] if options.include? :conjecture
19
+ result = exec_spass(spass, options[:timeout] || SPASS_TIMEOUT)
20
+ if result == :inconclusive
21
+ puts "inconclusive result on testcase #{self.class.name}.#{method_name}"
22
+ else
23
+ assert_equal expected_result, result
24
+ end
25
+ rescue Exception => e
26
+ puts spass unless spass.nil?
27
+ raise e
28
+ end
29
+
30
+ def spass_assert(expected_result, input, timeout = SPASS_TIMEOUT)
31
+ adsl_assert expected_result, input, :timeout => timeout
32
+ end
33
+ end