adsl 0.0.3 → 0.1.0

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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -20
  3. data/README.md +14 -21
  4. data/bin/adsl-verify +8 -8
  5. data/lib/adsl.rb +3 -0
  6. data/lib/adsl/adsl.rb +3 -0
  7. data/lib/adsl/ds/data_store_spec.rb +339 -0
  8. data/lib/adsl/extract/instrumenter.rb +206 -0
  9. data/lib/adsl/extract/meta.rb +33 -0
  10. data/lib/adsl/extract/rails/action_block_builder.rb +233 -0
  11. data/lib/adsl/extract/rails/action_instrumenter.rb +400 -0
  12. data/lib/adsl/extract/rails/action_runner.rb +57 -0
  13. data/lib/adsl/extract/rails/active_record_metaclass_generator.rb +555 -0
  14. data/lib/adsl/extract/rails/callback_chain_simulator.rb +135 -0
  15. data/lib/adsl/extract/rails/invariant_extractor.rb +48 -0
  16. data/lib/adsl/extract/rails/invariant_instrumenter.rb +70 -0
  17. data/lib/adsl/extract/rails/other_meta.rb +57 -0
  18. data/lib/adsl/extract/rails/rails_extractor.rb +211 -0
  19. data/lib/adsl/extract/rails/rails_instrumentation_test_case.rb +34 -0
  20. data/lib/adsl/extract/rails/rails_special_gem_instrumentation.rb +120 -0
  21. data/lib/adsl/extract/rails/rails_test_helper.rb +140 -0
  22. data/lib/adsl/extract/sexp_utils.rb +54 -0
  23. data/lib/adsl/fol/first_order_logic.rb +261 -0
  24. data/lib/adsl/parser/adsl_parser.racc +159 -0
  25. data/lib/{parser → adsl/parser}/adsl_parser.rex +4 -4
  26. data/lib/{parser → adsl/parser}/adsl_parser.rex.rb +6 -6
  27. data/lib/adsl/parser/adsl_parser.tab.rb +1031 -0
  28. data/lib/adsl/parser/ast_nodes.rb +1410 -0
  29. data/lib/adsl/railtie.rb +67 -0
  30. data/lib/adsl/spass/bin.rb +230 -0
  31. data/lib/{spass → adsl/spass}/ruby_extensions.rb +0 -0
  32. data/lib/adsl/spass/spass_ds_extensions.rb +931 -0
  33. data/lib/adsl/spass/spass_translator.rb +393 -0
  34. data/lib/adsl/spass/util.rb +13 -0
  35. data/lib/adsl/util/csv_hash_formatter.rb +94 -0
  36. data/lib/adsl/util/general.rb +228 -0
  37. data/lib/adsl/util/test_helper.rb +71 -0
  38. data/lib/adsl/verification/formula_generators.rb +231 -0
  39. data/lib/adsl/verification/instrumentation_filter.rb +50 -0
  40. data/lib/adsl/verification/invariant.rb +19 -0
  41. data/lib/adsl/verification/rails_verification.rb +33 -0
  42. data/lib/adsl/verification/utils.rb +20 -0
  43. data/lib/adsl/verification/verification_case.rb +13 -0
  44. data/test/integration/rails/rails_branch_verification_test.rb +112 -0
  45. data/test/integration/rails/rails_verification_test.rb +253 -0
  46. data/test/integration/spass/basic_translation_test.rb +563 -0
  47. data/test/integration/spass/control_flow_translation_test.rb +421 -0
  48. data/test/unit/adsl/ds/data_store_spec_test.rb +54 -0
  49. data/test/unit/adsl/extract/instrumenter_test.rb +103 -0
  50. data/test/unit/adsl/extract/meta_test.rb +142 -0
  51. data/test/unit/adsl/extract/rails/action_block_builder_test.rb +178 -0
  52. data/test/unit/adsl/extract/rails/action_instrumenter_test.rb +68 -0
  53. data/test/unit/adsl/extract/rails/active_record_metaclass_generator_test.rb +336 -0
  54. data/test/unit/adsl/extract/rails/callback_chain_simulator_test.rb +76 -0
  55. data/test/unit/adsl/extract/rails/invariant_extractor_test.rb +92 -0
  56. data/test/unit/adsl/extract/rails/rails_extractor_test.rb +1380 -0
  57. data/test/unit/adsl/extract/rails/rails_test_helper_test.rb +25 -0
  58. data/test/unit/adsl/extract/sexp_utils_test.rb +100 -0
  59. data/test/unit/adsl/fol/first_order_logic_test.rb +227 -0
  60. data/test/unit/adsl/parser/action_parser_test.rb +1040 -0
  61. data/test/unit/adsl/parser/ast_nodes_test.rb +359 -0
  62. data/test/unit/adsl/parser/class_parser_test.rb +288 -0
  63. data/test/unit/adsl/parser/general_parser_test.rb +67 -0
  64. data/test/unit/adsl/parser/invariant_parser_test.rb +432 -0
  65. data/test/unit/adsl/parser/parser_util_test.rb +126 -0
  66. data/test/unit/adsl/spass/bin_test.rb +65 -0
  67. data/test/unit/adsl/spass/ruby_extensions_test.rb +39 -0
  68. data/test/unit/adsl/spass/spass_ds_extensions_test.rb +7 -0
  69. data/test/unit/adsl/spass/spass_translator_test.rb +342 -0
  70. data/test/unit/adsl/util/csv_hash_formatter_test.rb +68 -0
  71. data/test/unit/adsl/util/general_test.rb +303 -0
  72. data/test/unit/adsl/util/test_helper_test.rb +120 -0
  73. data/test/unit/adsl/verification/formula_generators_test.rb +200 -0
  74. data/test/unit/adsl/verification/instrumentation_filter_test.rb +39 -0
  75. data/test/unit/adsl/verification/utils_test.rb +39 -0
  76. data/test/unit/adsl/verification/verification_case_test.rb +8 -0
  77. metadata +229 -29
  78. data/lib/ds/data_store_spec.rb +0 -292
  79. data/lib/fol/first_order_logic.rb +0 -260
  80. data/lib/parser/adsl_ast.rb +0 -779
  81. data/lib/parser/adsl_parser.racc +0 -151
  82. data/lib/parser/adsl_parser.tab.rb +0 -976
  83. data/lib/parser/dsdl_parser.rex.rb +0 -196
  84. data/lib/parser/dsdl_parser.tab.rb +0 -976
  85. data/lib/spass/bin.rb +0 -164
  86. data/lib/spass/spass_ds_extensions.rb +0 -870
  87. data/lib/spass/spass_translator.rb +0 -388
  88. data/lib/spass/util.rb +0 -11
  89. data/lib/util/csv_hash_formatter.rb +0 -47
  90. data/lib/util/test_helper.rb +0 -33
  91. data/lib/util/util.rb +0 -114
@@ -1,388 +0,0 @@
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 common_name
273
- registered_names = (@functions + @predicates).map{ |a| a.name }
274
- prefix = common_name
275
- prefix = common_name.scan(/^(.+)_\d+$/).first.first if prefix =~ /^.+_\d+$/
276
- regexp = /^#{ Regexp.escape prefix }(?:_(\d+))?$/
277
-
278
- already_registered = registered_names.select{ |a| a =~ regexp }
279
- return common_name if already_registered.empty?
280
-
281
- rhs_numbers = already_registered.map{ |a| [a, a.scan(regexp).first.first] }
282
-
283
- rhs_numbers.each do |a|
284
- a[1] = a[1].nil? ? -1 : a[1].to_i
285
- end
286
-
287
- max_name = rhs_numbers.max_by{ |a| a[1] }
288
- return max_name[0].increment_suffix
289
- end
290
-
291
- def _reserve_names(*names)
292
- result = []
293
- names.each do |name|
294
- if name.is_a? Array
295
- result << _reserve_names(*name)
296
- else
297
- while @temp_vars.include? name
298
- name = name.to_s.increment_suffix.to_sym
299
- end
300
- @temp_vars.push name
301
- result << name
302
- end
303
- end
304
- result
305
- end
306
-
307
- def reserve_names(*names)
308
- result = _reserve_names(*names)
309
- yield *result
310
- ensure
311
- names.flatten.length.times do
312
- @temp_vars.pop
313
- end
314
- end
315
-
316
- def gen_formula_for_unique_arg(pred, *args)
317
- individuals = []
318
- args.each do |arg|
319
- arg = arg.is_a?(Range) ? arg.to_a : [arg].flatten
320
- next if arg.empty?
321
- vars1 = (1..pred.arity).map{ |i| "e#{i}" }
322
- vars2 = vars1.dup
323
- as = []
324
- bs = []
325
- arg.each do |index|
326
- a = "a#{index+1}".to_sym
327
- vars1[index] = a
328
- b = "b#{index+1}".to_sym
329
- vars2[index] = b
330
- as << a
331
- bs << b
332
- end
333
- reserve_names (vars1 | vars2) do
334
- individuals << _for_all(vars1 | vars2, _implies(_and(pred[vars1], pred[vars2]), _pairwise_equal(as, bs)))
335
- end
336
- end
337
- return true if individuals.empty?
338
- formula = _and(individuals)
339
- create_formula formula
340
- return formula
341
- end
342
-
343
- def spass_wrap(with, what)
344
- return "" if what.length == 0
345
- return with % what
346
- end
347
-
348
- def spass_list_of(what, *content)
349
- spass_wrap "list_of_#{what.to_s}.%s\nend_of_list.", content.flatten.map{ |c| "\n " + c.to_s }.join("")
350
- end
351
-
352
- def to_spass_string
353
- functions = @functions.map{ |f| "(#{f.name}, #{f.arity})" }.join(", ")
354
- predicates = @predicates.map{ |p| "(#{p.name}, #{p.arity})" }.join(", ")
355
- formulae = @formulae.first.map do |f|
356
- begin
357
- next "formula(#{f.resolve_spass})."
358
- rescue => e
359
- pp f
360
- raise e
361
- end
362
- end
363
- conjectures = @conjectures.map{ |f| "formula(#{f.resolve_spass})." }
364
- <<-SPASS
365
- begin_problem(Blahblah).
366
- list_of_descriptions.
367
- name({* *}).
368
- author({* *}).
369
- status(satisfiable).
370
- description( {* *} ).
371
- end_of_list.
372
- #{spass_list_of( :symbols,
373
- spass_wrap("functions[%s].", functions),
374
- spass_wrap("predicates[%s].", predicates)
375
- )}
376
- #{spass_list_of( "formulae(axioms)",
377
- formulae
378
- )}
379
- #{spass_list_of( "formulae(conjectures)",
380
- conjectures
381
- )}
382
- end_problem.
383
- SPASS
384
- end
385
-
386
- end
387
- end
388
-
@@ -1,11 +0,0 @@
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
@@ -1,47 +0,0 @@
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(*cols)
17
- @row_hashes = []
18
- @columns = []
19
- cols.each do |col|
20
- add_column col
21
- end
22
- end
23
-
24
- def add_row(row)
25
- @row_hashes << row
26
- row.keys.each do |key|
27
- add_column key unless @columns.include? key
28
- end
29
- end
30
-
31
- def add_column(col)
32
- raise "Duplicate column name #{col}" if @columns.include? col.to_sym
33
- @columns << col.to_sym
34
- end
35
-
36
- alias_method :<<, :add_row
37
-
38
- def to_s
39
- return '' if @columns.empty?
40
- output = @columns.map{ |c| escape(c) }.join(',') + "\n"
41
- @row_hashes.each do |row|
42
- output = output + @columns.map{ |c| escape(row[c] || '') }.join(',') + "\n"
43
- end
44
- output
45
- end
46
- end
47
- end
@@ -1,33 +0,0 @@
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