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
@@ -0,0 +1,67 @@
1
+ require 'adsl'
2
+ require 'rails'
3
+ require 'adsl/verification/rails_verification'
4
+
5
+ module ADSL
6
+ class Railtie < Rails::Railtie
7
+
8
+ def extract_arg(key)
9
+ regex = /^#{key}\s*=\s*(.+)$/
10
+ ARGV[1..-1].each do |arg|
11
+ return arg.match(regex)[1] if regex =~ arg
12
+ end
13
+ nil
14
+ end
15
+
16
+ def extract_actions_param_from_args
17
+ actions = ("#{extract_arg('ACTION') },#{extract_arg('ACTIONS') }").split(',').map(&:strip).reject(&:empty?)
18
+ controllers = ("#{extract_arg('CONTROLLER')},#{extract_arg('CONTROLLERS')}").split(',').map(&:strip).reject(&:empty?)
19
+ if actions.empty? and controllers.empty?
20
+ nil
21
+ elsif actions.empty?
22
+ controllers
23
+ elsif controllers.empty?
24
+ actions
25
+ else
26
+ action_controllers = []
27
+ actions.each do |a|
28
+ controllers.each do |c|
29
+ action_controllers << "#{c}__#{a}"
30
+ end
31
+ end
32
+ action_controllers
33
+ end
34
+ end
35
+
36
+ include ADSL::Verification::RailsVerification
37
+
38
+ railtie_name :adsl
39
+
40
+ rake_tasks do
41
+ desc 'Verify Rails app logic'
42
+ task :verify => :environment do
43
+ verify_options = {}
44
+
45
+ actions = extract_actions_param_from_args
46
+ verify_options[:actions] = actions unless actions.nil?
47
+
48
+ output = extract_arg('OUTPUT_FORMAT')
49
+ unless output.nil? || output.strip.empty?
50
+ verify_options[:output] = output.strip.downcase.to_sym
51
+ end
52
+
53
+ verify_spass :verify_options => verify_options
54
+ end
55
+
56
+ desc 'Translate Rails app into ADSL'
57
+ task :adsl_translate => :environment do
58
+ verify_options = {}
59
+
60
+ actions = extract_actions_param_from_args
61
+ verify_options[:actions] = actions unless actions.nil?
62
+
63
+ adsl_translate :verify_options => verify_options
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,230 @@
1
+ require 'tempfile'
2
+ require 'colorize'
3
+ require 'adsl/parser/adsl_parser.tab'
4
+ require 'adsl/ds/data_store_spec'
5
+ require 'adsl/spass/spass_ds_extensions'
6
+ require 'adsl/spass/util'
7
+ require 'adsl/util/general'
8
+ require 'adsl/util/csv_hash_formatter'
9
+
10
+ module ADSL
11
+ module Spass
12
+ module Bin
13
+ include Spass::Util
14
+
15
+ def default_options
16
+ {
17
+ :halt_on_error => false,
18
+ :check_satisfiability => true,
19
+ :timeout => 180,
20
+ :output => :terminal,
21
+ :actions => nil,
22
+ :invariants => nil
23
+ }
24
+ end
25
+
26
+ def output(term_msg, csv)
27
+ if @verification_output == :terminal
28
+ if term_msg
29
+ print term_msg
30
+ STDOUT.flush
31
+ end
32
+ elsif @verification_output == :csv
33
+ @csv_output << csv if csv
34
+ elsif @verification_output == :silent
35
+ else
36
+ raise "Unknown verification output #{@verification_output}"
37
+ end
38
+ end
39
+
40
+ def filter_by_name(elems, names)
41
+ return elems if names.nil?
42
+ elems.select{ |elem| names.map{ |name| elem.name.include? name}.include? true }
43
+ end
44
+
45
+ def remove_empty_actions(actions)
46
+ empty, valid = actions.select_reject{ |a| a.block.statements.empty? }
47
+ unless empty.empty?
48
+ if @verification_output == :terminal
49
+ puts 'Actions with empty bodies trivially preserve invariants.'
50
+ puts "The following actions are empty: #{ empty.map(&:name).join ', ' }"
51
+ elsif @verification_output == :csv
52
+ empty.each do |empty_action|
53
+ @csv_output << {
54
+ :action => empty_action.name,
55
+ :result => 'trivially correct'
56
+ }
57
+ end
58
+ end
59
+ end
60
+ actions.set_to valid
61
+ end
62
+
63
+ def verify(input, options={})
64
+ ds_spec = if input.is_a? String
65
+ ADSL::Parser::ADSLParser.new.parse input
66
+ elsif input.is_a? ADSL::Parser::ASTNode
67
+ input.typecheck_and_resolve
68
+ elsif input.is_a? ADSL::DS::DSSpec
69
+ input
70
+ end
71
+
72
+ options = default_options.merge options
73
+
74
+ stop_on_incorrect = options[:halt_on_error]
75
+ check_satisfiability = options[:check_satisfiability]
76
+ timeout = options[:timeout] || -1
77
+ actions = filter_by_name ds_spec.actions, options[:actions]
78
+ invariants = filter_by_name ds_spec.invariants, options[:invariants]
79
+
80
+ @csv_output = ::ADSL::Util::CSVHashFormatter.new
81
+
82
+ options[:output] = options[:output].to_sym unless options[:output].nil?
83
+ raise "Unknown verification format `#{options[:output]}'" unless [nil, :terminal, :csv, :silent].include? options[:output]
84
+ @verification_output = options[:output]
85
+ do_stats = @verification_output == :csv
86
+
87
+ actions = remove_empty_actions actions
88
+
89
+ if check_satisfiability
90
+ begin
91
+ output "Checking for satisfiability...", nil
92
+
93
+ translation = nil
94
+ translation_time = Time.time_execution do
95
+ translation = ds_spec.translate_action nil
96
+ end
97
+
98
+ result, stats = exec_spass translation, timeout, true
99
+ if do_stats
100
+ stats[:translation_time] = translation_time
101
+ stats[:action] = '<unsatisfiability>'
102
+ stats[:result] = result.to_s
103
+ if input.is_a? ADSL::Parser::ASTNode
104
+ stats[:pre_optimize_size] = input.adsl_ast_size :pre_optimize => true
105
+ stats[:adsl_ast_size] = input.adsl_ast_size
106
+ end
107
+ end
108
+
109
+ if result == :correct
110
+ output "\rSatisfiability check #{ 'failed!'.red } ", stats
111
+ return false
112
+ elsif result == :inconclusive
113
+ output "\rSatisfiability check #{ 'inconclusive'.yellow } ", stats
114
+ else
115
+ output "\rSatisfiability check #{ 'passed'.green }. ", stats
116
+ end
117
+ ensure
118
+ output "\n", nil
119
+ end
120
+ end
121
+
122
+ all_correct = true
123
+ actions.each do |action|
124
+ invariants.each do |invariant|
125
+ output "Verifying action '#{action.name}' with invariant '#{invariant.name}'...", nil
126
+ begin
127
+ translation = nil
128
+ translation_time = Time.time_execution do
129
+ translation = ds_spec.translate_action action.name, invariant
130
+ end
131
+ result, stats = exec_spass translation, timeout, do_stats
132
+ if do_stats
133
+ stats[:translation_time] = translation_time
134
+ stats[:action] = action.name
135
+ stats[:invariant] = invariant.name
136
+ stats[:result] = result.to_s
137
+ if input.is_a? ADSL::Parser::ASTNode
138
+ stats[:adsl_ast_size] = input.adsl_ast_size(
139
+ :action_name => action.name,
140
+ :invariant_name => invariant.name
141
+ )
142
+ stats[:pre_optimize_size] = input.adsl_ast_size(
143
+ :pre_optimize => true,
144
+ :action_name => action.name,
145
+ :invariant_name => invariant.name
146
+ )
147
+ end
148
+ end
149
+
150
+ case result
151
+ when :correct
152
+ output "\rAction '#{action.name}' with invariant '#{invariant.name}': #{ 'correct'.green } ", stats
153
+ when :incorrect
154
+ output "\rAction '#{action.name}' with invariant '#{invariant.name}': #{ 'incorrect'.red } ", stats
155
+ all_correct = false
156
+ return false if stop_on_incorrect
157
+ when :inconclusive
158
+ output "\rAction '#{action.name}' with invariant '#{invariant.name}': #{ 'inconclusive'.yellow } ", stats
159
+ all_correct = false
160
+ else
161
+ raise "Unknown exec_spass result: #{result}"
162
+ end
163
+ ensure
164
+ output "\n", nil
165
+ end
166
+ end
167
+ end
168
+ return all_correct
169
+ ensure
170
+ puts @csv_output.sort!(:action, :invariant).to_s if @verification_output == :csv
171
+ @csv_output = nil
172
+ end
173
+
174
+ def exec_spass(spass_code, timeout=-1, include_stats = false)
175
+ tmp_file = Tempfile.new "spass_temp"
176
+ tmp_file.write spass_code
177
+ tmp_file.close
178
+ arg_combos = ["", "-Sorts=0"]
179
+ commands = arg_combos.map{ |a| "SPASS #{a} -TimeLimit=#{timeout} #{tmp_file.path}" }
180
+ output = process_race(*commands)
181
+ result = /^SPASS beiseite: (.+)\.$/.match(output)[1]
182
+
183
+ stats = include_stats ? pack_stats(spass_code, output) : nil
184
+ verdict = nil
185
+
186
+ case result
187
+ when 'Proof found'
188
+ verdict = :correct
189
+ when 'Completion found'
190
+ verdict = :incorrect
191
+ else
192
+ verdict = :inconclusive
193
+ end
194
+ return stats.nil? ? verdict : [verdict, stats]
195
+ ensure
196
+ tmp_file.delete unless tmp_file.nil?
197
+ end
198
+
199
+ def pack_stats(spass_code, spass_output)
200
+ spass_output = spass_output.split("\n").last(10).join("\n")
201
+ stats = {}
202
+
203
+ # should be set externally
204
+ stats[:translation_time] = nil
205
+ stats[:action] = nil
206
+ stats[:invariant] = nil
207
+ stats[:result] = nil
208
+
209
+ identifiers = /predicates\s*\[([^\]]*)\]/.match(spass_code)[1].scan(/\w+/)
210
+ stats[:spass_predicate_count] = identifiers.length
211
+
212
+ formulae = spass_code.scan(/formula\s*\([^\.]+\)\./)
213
+ stats[:spass_formula_count] = formulae.length
214
+ stats[:average_formula_length] = formulae.inject(0){ |total, formula| total += formula.length} / formulae.length
215
+
216
+ times = spass_output.scan(/(\d):(\d\d):(\d\d)\.(\d\d)/)
217
+ raise "Incorrect time format extracted from spass output. Tail of spass output: #{spass_output}" if times.length != 6
218
+ times = times.map{ |time| time[3].to_i*10 + time[2].to_i*1000 + time[1].to_i*60*1000 + time[0].to_i*60*60*1000 }
219
+ stats[:spass_preparation_time] = times[1..2].sum
220
+ stats[:spass_proof_lookup_time] = times[3..5].sum
221
+
222
+ stats[:proof_clause_count] = /^SPASS derived (\d+) clauses.*$/.match(spass_output)[1].to_i
223
+
224
+ stats[:memory] = /^\s*SPASS allocated (\d+) KBytes.*$/.match(spass_output)[1].to_i
225
+
226
+ stats
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,931 @@
1
+ require 'adsl/fol/first_order_logic'
2
+ require 'adsl/spass/spass_translator'
3
+
4
+ module ADSL
5
+ module DS
6
+ class DSNode
7
+ def replace_var(from, to); end
8
+ end
9
+
10
+ class DSSpec < DSNode
11
+ def translate_action(action_name, *listed_invariants)
12
+ translation = ADSL::Spass::SpassTranslator::Translation.new
13
+
14
+ if action_name
15
+ action = @actions.select{ |a| a.name == action_name }
16
+ raise ArgumentError, "Action '#{action_name}' not found" if action.empty?
17
+ action = action.first
18
+ end
19
+
20
+ translation.classes.concat @classes
21
+ @classes.each do |klass|
22
+ klass.translate(translation)
23
+ end
24
+
25
+ relations = @classes.map{ |c| c.relations }.flatten
26
+ relations.select{ |r| r.inverse_of.nil? }.each do |relation|
27
+ relation.translate(translation)
28
+ end
29
+ relations.select{ |r| r.inverse_of }.each do |relation|
30
+ relation.translate(translation)
31
+ end
32
+
33
+ action.prepare(translation) if action_name
34
+
35
+ translation.create_formula FOL::ForAll.new(:o, FOL::Equiv.new(
36
+ translation.is_object[:o],
37
+ FOL::Or.new(@classes.map{ |c| c[:o] })
38
+ ))
39
+ @classes.group_by{ |klass| klass.parent }.each do |common_parent, children|
40
+ unless common_parent.nil?
41
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
42
+ FOL::Or.new(children.map{ |c| c[:o] }),
43
+ common_parent[:o]
44
+ ))
45
+ end
46
+ if children.length > 1
47
+ children.each do |child|
48
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
49
+ child[:o],
50
+ FOL::Not.new(children.select{ |c| c != child}.map{ |c| c[:o] })
51
+ ))
52
+ end
53
+ end
54
+ end
55
+
56
+ relations = @classes.map{ |c| c.relations }.flatten.map{ |rel| rel.type_pred }.uniq
57
+ translation.create_formula FOL::ForAll.new(:o, FOL::Equiv.new(
58
+ translation.is_tuple[:o],
59
+ FOL::Or.new(relations.map{ |r| r[:o] })
60
+ ))
61
+ if relations.length > 1
62
+ relations.each do |relation|
63
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
64
+ relation[:o],
65
+ FOL::Not.new(relations.select{ |c| c != relation}.map{ |c| c[:o] })
66
+ ))
67
+ end
68
+ end
69
+
70
+ translation.create_formula FOL::ForAll.new(:o, FOL::OneOf.new(
71
+ translation.is_object[:o],
72
+ translation.is_tuple[:o],
73
+ translation.is_either_resolution[:o]
74
+ ))
75
+ translation.create_formula FOL::ForAll.new(:o, FOL::Implies.new(
76
+ translation.existed_initially[:o],
77
+ FOL::Or.new(translation.is_object[:o], translation.is_tuple[:o])
78
+ ))
79
+ translation.create_formula FOL::ForAll.new(:o, FOL::Equiv.new(
80
+ translation.existed_initially[:o],
81
+ translation.prev_state[:o]
82
+ ))
83
+
84
+ action.translate(translation) if action_name
85
+
86
+ if action_name
87
+ translation.invariant_state = translation.existed_initially
88
+ pre_invariants = @invariants.map{ |invariant| invariant.formula.resolve_invariant_formula(translation) }
89
+
90
+ listed_invariants = @invariants if listed_invariants.empty?
91
+ translation.invariant_state = translation.exists_finally
92
+ post_invariants = listed_invariants.map{ |invariant| invariant.formula.resolve_invariant_formula(translation) }
93
+
94
+ translation.create_conjecture FOL::Implies.new(
95
+ FOL::And.new(pre_invariants),
96
+ FOL::And.new(post_invariants)
97
+ ).resolve_spass
98
+ else
99
+ # used to check for contradictions in the model
100
+ invariants_formulae = invariants.map do |invariant|
101
+ formula = invariant.formula
102
+ dummy_state = translation.create_predicate 'always_true', 1
103
+ translation.create_formula FOL::ForAll.new(:o, dummy_state[:o])
104
+ translation.invariant_state = dummy_state
105
+ formula.resolve_invariant_formula(translation)
106
+ end
107
+ translation.create_conjecture FOL::Not.new(FOL::And.new(invariants_formulae))
108
+ end
109
+
110
+ return translation.to_spass_string
111
+ end
112
+ end
113
+
114
+ class DSAction < DSNode
115
+ include FOL
116
+
117
+ def prepare(translation)
118
+ @args.each do |arg|
119
+ arg.define_predicate translation
120
+ end
121
+ @block.prepare translation
122
+ end
123
+
124
+ def translate(translation)
125
+ translation.context = translation.root_context
126
+
127
+ @args.length.times do |i|
128
+ cardinality = @cardinalities[i]
129
+ arg = @args[i]
130
+
131
+ translation.create_formula _for_all(:o, _implies(
132
+ arg[:o],
133
+ _and(translation.existed_initially[:o], arg.type[:o])
134
+ ))
135
+
136
+ translation.create_formula _exists(:o, arg[:o]) if cardinality[0] > 0
137
+ if cardinality[1] == 1
138
+ translation.create_formula _for_all(:o1, :o2, _implies(
139
+ _and(arg[:o1], arg[:o2]),
140
+ _equal(:o1, :o2)
141
+ ))
142
+ end
143
+ end
144
+
145
+ @block.migrate_state_spass translation
146
+
147
+ translation.create_formula _for_all(:o, _equiv(
148
+ translation.exists_finally[:o],
149
+ translation.prev_state[:o]
150
+ ))
151
+ end
152
+ end
153
+
154
+ class DSClass < DSNode
155
+ def [](variable)
156
+ @type_pred[variable]
157
+ end
158
+
159
+ def translate(translation)
160
+ @type_pred = translation.create_predicate "of_#{@name}_type", 1
161
+ end
162
+ end
163
+
164
+ class DSRelation < DSNode
165
+ include FOL
166
+ attr_reader :type_pred, :left_link, :right_link
167
+
168
+ def [](variable)
169
+ @type_pred[variable]
170
+ end
171
+
172
+ def translate(translation)
173
+ if @inverse_of
174
+ @type_pred = @inverse_of.type_pred
175
+ @left_link = @inverse_of.right_link
176
+ @right_link = @inverse_of.left_link
177
+ else
178
+ @type_pred = translation.create_predicate "of_#{@from_class.name}_#{@name}_type", 1
179
+ @left_link = translation.create_predicate "left_link_#{@from_class.name}_#{@name}", 2
180
+ @right_link = translation.create_predicate "right_link_#{@from_class.name}_#{@name}", 2
181
+ translation.create_formula _for_all(:t, :o, _implies(@left_link[:t, :o], _and(@from_class[:o], @type_pred[:t])))
182
+ translation.create_formula _for_all(:t, :o, _implies(@right_link[:t, :o], _and(@to_class[:o], @type_pred[:t])))
183
+ translation.create_formula _for_all(:t, :o1, :o2, _and(
184
+ _implies(_and(@left_link[:t, :o1], @left_link[:t, :o2]), _equal(:o1, :o2)),
185
+ _implies(_and(@right_link[:t, :o1], @right_link[:t, :o2]), _equal(:o1, :o2))
186
+ ))
187
+ translation.create_formula _for_all(:t, _implies(
188
+ @type_pred[:t],
189
+ _exists(:o1, :o2, _and(@left_link[:t, :o1], @right_link[:t, :o2]))
190
+ ))
191
+ end
192
+
193
+ if @cardinality[0] > 0
194
+ translation.create_formula _for_all(:o, _implies(@from_class[:o], _exists(:t, @left_link[:t, :o])))
195
+ end
196
+ if @cardinality[1] == 1
197
+ translation.create_formula _for_all(:o, :t1, :t2, _implies(
198
+ _and(@left_link[:t1, :o], @left_link[:t2, :o]),
199
+ _equal(:t1, :t2)
200
+ ))
201
+ end
202
+ end
203
+ end
204
+
205
+ class DSCreateObj < DSNode
206
+ include FOL
207
+ attr_reader :context_creation_link, :context
208
+
209
+ def prepare(translation)
210
+ @context = translation.context
211
+ translation.create_obj_stmts[@klass] << self
212
+ @context_creation_link = translation.create_predicate "created_#{@klass.name}_in_context", context.level + 1
213
+ end
214
+
215
+ def migrate_state_spass(translation)
216
+ state = translation.create_state "post_create_#{@klass.name}"
217
+ prev_state = translation.prev_state
218
+ translation.gen_formula_for_unique_arg(@context_creation_link, (0..@context.level-1), @context.level)
219
+ translation.reserve_names @context.p_names, :o do |ps, o|
220
+ created_by_other_create_stmts = translation.create_obj_stmts[@klass].select{|s| s != self}.map do |stmt|
221
+ formula = nil
222
+ translation.reserve_names stmt.context.p_names do |other_ps|
223
+ formula = _exists(other_ps, stmt.context_creation_link[other_ps, o])
224
+ end
225
+ formula
226
+ end
227
+ created_by_other_create_stmts << translation.existed_initially[o]
228
+ child_classes = translation.classes.select{ |c| c.parent == @klass }
229
+ not_of_child_types = child_classes.empty? ? true : _and(child_classes.map{ |c| _not(c[o]) })
230
+ translation.create_formula _for_all(ps, o, _implies(
231
+ @context_creation_link[ps, o], _and(
232
+ context.type_pred(ps),
233
+ _not(created_by_other_create_stmts),
234
+ @klass[o],
235
+ not_of_child_types
236
+ )
237
+ ))
238
+ translation.create_formula _for_all(ps, _implies(@context.type_pred(ps), _exists(o,
239
+ @context_creation_link[ps, o]
240
+ )))
241
+ translation.create_formula _for_all(ps, o,
242
+ _if_then_else_eq(
243
+ @context_creation_link[ps, o],
244
+ _and(_not(prev_state[ps, o]), state[ps, o]),
245
+ _equiv(prev_state[ps, o], state[ps, o])
246
+ )
247
+ )
248
+
249
+ relevant_from_relations = translation.classes.map{ |c| c.relations }.flatten.select{ |r| r.from_class == @klass }
250
+ relevant_to_relations = translation.classes.map{ |c| c.relations }.flatten.select{ |r| r.to_class == @klass }
251
+ translation.reserve_names :r do |r|
252
+ translation.create_formula _for_all(ps, o, _implies(
253
+ @context_creation_link[ps, o],
254
+ _for_all(r, _not(_and(
255
+ state[ps, r],
256
+ _or(
257
+ relevant_from_relations.map{ |rel| rel.left_link[r, o] },
258
+ relevant_to_relations.map{ |rel| rel.right_link[r, o] }
259
+ )
260
+ )))
261
+ ))
262
+ end
263
+ end
264
+
265
+ translation.prev_state = state
266
+ end
267
+ end
268
+
269
+ class DSCreateObjset < DSNode
270
+ include FOL
271
+
272
+ def prepare_action(translation); end
273
+
274
+ def resolve_action_objset(translation, ps, var)
275
+ return @createobj.context_creation_link[ps, var]
276
+ end
277
+ end
278
+
279
+ class DSDeleteObj < DSNode
280
+ include FOL
281
+ attr_accessor :context_deletion_link
282
+
283
+ def prepare(translation)
284
+ @objset.prepare_action translation
285
+ end
286
+
287
+ def migrate_state_spass(translation)
288
+ state = translation.create_state "post_delete_#{@objset.type.name}"
289
+ prev_state = translation.prev_state
290
+ context = translation.context
291
+
292
+ translation.reserve_names context.p_names, :o do |ps, o|
293
+ translation.create_formula _for_all(ps, o,
294
+ _if_then_else_eq(_and(@objset.resolve_action_objset(translation, ps, o), prev_state[ps, o]),
295
+ _and(prev_state[ps, o], _not(state[ps, o])),
296
+ _equiv(prev_state[ps, o], state[ps, o])
297
+ )
298
+ )
299
+ end
300
+
301
+ translation.prev_state = state
302
+ end
303
+ end
304
+
305
+ class DSCreateTup < DSNode
306
+ include FOL
307
+
308
+ def prepare(translation)
309
+ @objset1.prepare_action translation
310
+ @objset2.prepare_action translation
311
+ end
312
+
313
+ def migrate_state_spass(translation)
314
+ return if @objset1.type.nil? or @objset2.type.nil?
315
+
316
+ state = translation.create_state "post_create_#{@relation.from_class.name}_#{@relation.name}"
317
+ prev_state = translation.prev_state
318
+ context = translation.context
319
+
320
+ translation.reserve_names context.p_names, :r, :o1, :o2 do |ps, r, o1, o2|
321
+ objset1 = @objset1.resolve_action_objset(translation, ps, o1)
322
+ objset2 = @objset2.resolve_action_objset(translation, ps, o2)
323
+ translation.create_formula FOL::ForAll.new(ps, r, FOL::Implies.new(
324
+ context.type_pred(ps),
325
+ FOL::Equiv.new(
326
+ state[ps, r],
327
+ FOL::Or.new(
328
+ prev_state[ps, r],
329
+ FOL::Exists.new(o1, o2, FOL::And.new(
330
+ prev_state[ps, o1], prev_state[ps, o2],
331
+ @relation.left_link[r, o1], @relation.right_link[r, o2],
332
+ objset1, objset2
333
+ ))
334
+ )
335
+ )
336
+ ))
337
+ translation.create_formula FOL::ForAll.new(ps, o1, o2, FOL::Implies.new(
338
+ FOL::And.new(prev_state[ps, o1], prev_state[ps, o2], objset1, objset2),
339
+ FOL::Exists.new(r, FOL::And.new(state[ps, r], @relation.left_link[r, o1], @relation.right_link[r, o2]))
340
+ ))
341
+ end
342
+ translation.prev_state = state
343
+ end
344
+ end
345
+
346
+ class DSDeleteTup < DSNode
347
+ include FOL
348
+
349
+ def prepare(translation)
350
+ @objset1.prepare_action translation
351
+ @objset2.prepare_action translation
352
+ end
353
+
354
+ def migrate_state_spass(translation)
355
+ return if @objset1.type.nil? or @objset2.type.nil?
356
+
357
+ state = translation.create_state "post_deleteref_#{@relation.from_class.name}_#{@relation.name}"
358
+ prev_state = translation.prev_state
359
+ context = translation.context
360
+
361
+ translation.reserve_names context.p_names, :r, :o1, :o2 do |ps, r, o1, o2|
362
+ objset1 = @objset1.resolve_action_objset(translation, ps, o1)
363
+ objset2 = @objset2.resolve_action_objset(translation, ps, o2)
364
+ translation.create_formula FOL::ForAll.new(ps, r, FOL::Equiv.new(
365
+ state[ps, r],
366
+ FOL::And.new(
367
+ prev_state[ps, r],
368
+ FOL::ForAll.new(o1, o2, FOL::Not.new(FOL::And.new(
369
+ objset1, objset2,
370
+ prev_state[ps, o1], prev_state[ps, o2],
371
+ @relation.left_link[r, o1], @relation.right_link[r, o2]
372
+ )))
373
+ )
374
+ ))
375
+ end
376
+
377
+ translation.prev_state = state
378
+ end
379
+ end
380
+
381
+ class DSEither < DSNode
382
+ include FOL
383
+ attr_reader :resolution_link, :is_trues
384
+
385
+ def prepare(translation)
386
+ context = translation.context
387
+ @resolution_link = translation.create_predicate :resolution_link, context.level+1
388
+ translation.reserve_names context.p_names, :r do |ps, r|
389
+ translation.gen_formula_for_unique_arg(@resolution_link, (0..ps.length-1), ps.length)
390
+ translation.create_formula _for_all(ps, r, _implies(@resolution_link[ps, r], _and(
391
+ translation.context.type_pred(ps),
392
+ translation.is_either_resolution[r]
393
+ )))
394
+ end
395
+ @is_trues = []
396
+ @blocks.length.times do |i|
397
+ is_trues << translation.create_predicate("either_resolution_#{i}_is_true", 1)
398
+ end
399
+ @blocks.each do |block|
400
+ block.prepare(translation)
401
+ end
402
+ end
403
+
404
+ def migrate_state_spass(translation)
405
+ post_state = translation.create_state :post_either
406
+ prev_state = translation.prev_state
407
+ context = translation.context
408
+
409
+ pre_states = []
410
+ post_states = []
411
+ @blocks.length.times do |i|
412
+ pre_states << translation.create_state(:pre_of_either)
413
+ end
414
+ translation.create_formula FOL::ForAll.new(:r, FOL::Implies.new(
415
+ translation.is_either_resolution[:r],
416
+ FOL::OneOf.new(is_trues.map{ |pred| pred[:r] })
417
+ ))
418
+
419
+ translation.reserve_names context.p_names, :resolution, :o do |ps, resolution, o|
420
+ translation.create_formula FOL::ForAll.new(ps, FOL::Implies.new(
421
+ translation.context.type_pred(ps),
422
+ FOL::Exists.new(resolution, FOL::And.new(
423
+ @resolution_link[ps, resolution],
424
+ FOL::And.new((0..@blocks.length-1).map { |i|
425
+ FOL::Equiv.new(is_trues[i][resolution], FOL::ForAll.new(o, FOL::Equiv.new(prev_state[ps, o], pre_states[i][ps, o])))
426
+ })
427
+ ))
428
+ ))
429
+ end
430
+
431
+ @blocks.length.times do |i|
432
+ translation.prev_state = pre_states[i]
433
+ @blocks[i].migrate_state_spass translation
434
+ post_states << translation.prev_state
435
+ end
436
+
437
+ translation.reserve_names context.p_names, :resolution, :o do |ps, resolution, o|
438
+ translation.create_formula FOL::ForAll.new(ps, FOL::Implies.new(
439
+ translation.context.type_pred(ps),
440
+ FOL::Exists.new(resolution, FOL::And.new(
441
+ @resolution_link[ps, resolution],
442
+ FOL::And.new((0..@blocks.length-1).map { |i|
443
+ FOL::Equiv.new(is_trues[i][resolution], FOL::ForAll.new(o, FOL::Equiv.new(post_state[ps, o], post_states[i][ps, o])))
444
+ })
445
+ ))
446
+ ))
447
+ end
448
+
449
+ translation.prev_state = post_state
450
+ end
451
+ end
452
+
453
+ class DSEitherLambdaObjset < DSNode
454
+ def prepare_action(translation); end
455
+
456
+ def resolve_action_objset(translation, ps, o)
457
+ translation.reserve_names :r do |r|
458
+ implications = []
459
+ @either.blocks.length.times do |i|
460
+ implications << FOL::Implies.new(@either.is_trues[i][r], @vars[i].resolve_action_objset(translation, ps, o))
461
+ end
462
+
463
+ return FOL::ForAll.new(:r, FOL::Implies.new(
464
+ @either.resolution_link[ps, r],
465
+ FOL::And.new(implications)
466
+ ))
467
+ end
468
+ end
469
+ end
470
+
471
+ class DSForEachCommon < DSNode
472
+ include FOL
473
+
474
+ attr_reader :context, :pre_iteration_state, :post_iteration_state, :pre_state, :post_state
475
+
476
+ def prepare_with_context(translation, flat_context)
477
+ @context = translation.create_context "for_each_context", flat_context, translation.context
478
+ @objset.prepare_action translation
479
+ translation.context = @context
480
+ @block.prepare translation
481
+ translation.context = @context.parent
482
+ end
483
+
484
+ def migrate_state_spass(translation)
485
+ return if @objset.type.nil?
486
+
487
+ @pre_state = translation.prev_state
488
+ @post_state = translation.create_state :post_for_each
489
+
490
+ translation.reserve_names @context.parent.p_names, :o do |ps, o|
491
+ translation.create_formula _for_all(ps, o, _equiv(
492
+ _and(@objset.resolve_action_objset(translation, ps, o), @pre_state[ps, o]),
493
+ @context.type_pred(ps, o)
494
+ ))
495
+ end
496
+
497
+ translation.context = @context
498
+
499
+ @pre_iteration_state = translation.create_state :pre_iteration
500
+ @post_iteration_state = translation.create_state :post_iteration
501
+
502
+ translation.prev_state = @pre_iteration_state
503
+ @block.migrate_state_spass translation
504
+
505
+ translation.reserve_names @context.p_names, :o do |ps, o|
506
+ translation.create_formula _for_all(ps, o, _equiv(
507
+ translation.prev_state[ps, o],
508
+ @post_iteration_state[ps, o]
509
+ ))
510
+ end
511
+
512
+ create_iteration_formulae translation
513
+ translation.context = @context.parent
514
+ translation.prev_state = post_state
515
+ end
516
+ end
517
+
518
+ class DSForEach < DSForEachCommon
519
+ def prepare(translation)
520
+ prepare_with_context(translation, false)
521
+ end
522
+
523
+ def create_iteration_formulae(translation)
524
+ raise "Not implemented for flexible arities"
525
+ translation.create_formula _if_then_else_eq(
526
+ _exists(:c, :o, _and(old_state[:c, :o], @objset.resolve_action_objset(translation, :c, :o))),
527
+ _for_all(:parent, :c, :o, _implies(@context.parent_of_link[:parent, :c], _and(
528
+ _if_then_else(
529
+ @context.first[:parent, :c],
530
+ _equiv(old_state[:parent, :o], @pre_iteration_state[:c, :o]),
531
+ _for_all(:prev, _implies(
532
+ @context.just_before[:prev, :c],
533
+ _equiv(@post_iteration_state[:prev, :o], @pre_iteration_state[:c, :o])
534
+ ))
535
+ ),
536
+ _implies(
537
+ @context.last[:parent, :c],
538
+ _equiv(@post_state[:parent, :o], @post_iteration_state[:c, :o])
539
+ )
540
+ ))),
541
+ _for_all(:c, :o, _equiv(@pre_state[:c, :o], @post_state[:c, :o]))
542
+ )
543
+ end
544
+ end
545
+
546
+ class DSForEachIteratorObjset < DSNode
547
+ def prepare_action(translation); end
548
+
549
+ def resolve_action_objset(translation, ps, o)
550
+ return FOL::Equal.new(o, ps[@for_each.context.level-1])
551
+ end
552
+ end
553
+
554
+ class DSForEachPreLambdaObjset < DSNode
555
+ def prepare_action(translation); end
556
+
557
+ def resolve_action_objset(translation, ps, o)
558
+ raise "Not implemented for flexible arities"
559
+ translation.reserve_names :parent, :prev_context do |parent, prev_context|
560
+ return FOL::ForAll.new(parent, FOL::Implies.new(@context.parent_of_pred[parent, :c],
561
+ FOL::IfThenElseEq.new(
562
+ @context.first[parent, c],
563
+ @before_var[c, o],
564
+ FOL::Exists.new( prev_context, FOL::And.new(
565
+ @context.just_before[prev_context, c],
566
+ @inside_var[prev_context, o]
567
+ ))
568
+ )
569
+ ))
570
+ end
571
+ end
572
+ end
573
+
574
+ class DSFlatForEach < DSForEachCommon
575
+ def prepare(translation)
576
+ prepare_with_context(translation, true)
577
+ end
578
+
579
+ def create_iteration_formulae(translation)
580
+ context = translation.context
581
+ translation.reserve_names context.p_names, :o, :c1, :c2 do |ps, o, c1, c2|
582
+ ps_without_last = ps.first(ps.length - 1)
583
+ translation.create_formula _for_all(ps, o, _implies(
584
+ @context.type_pred(ps),
585
+ _equiv(@pre_state[ps_without_last, o], @pre_iteration_state[ps, o])
586
+ ))
587
+ translation.create_formula _for_all(ps_without_last, o, _if_then_else(
588
+ _not(_exists(ps.last, @context.type_pred(ps))),
589
+ _equiv(@pre_state[ps_without_last, o], @post_state[ps_without_last, o]),
590
+ _implies(
591
+ @context.parent.type_pred(ps_without_last),
592
+ _equiv(
593
+ @post_state[ps_without_last, o],
594
+ _or(
595
+ _and(
596
+ @pre_state[ps_without_last, o],
597
+ _for_all(ps.last, _implies(
598
+ @context.type_pred(ps),
599
+ @post_iteration_state[ps, o]
600
+ ))
601
+ ),
602
+ _and(
603
+ _not(@pre_state[ps_without_last, o]),
604
+ _exists(ps.last, @post_iteration_state[ps, o])
605
+ )
606
+ )
607
+ )
608
+ )
609
+ ))
610
+ end
611
+ end
612
+ end
613
+
614
+ class DSBlock < DSNode
615
+ def prepare(translation)
616
+ @statements.each do |stat|
617
+ stat.prepare translation
618
+ end
619
+ end
620
+
621
+ def migrate_state_spass(translation)
622
+ @statements.each do |stat|
623
+ stat.migrate_state_spass translation
624
+ end
625
+ end
626
+ end
627
+
628
+ class DSAssignment < DSNode
629
+ def prepare(translation)
630
+ @var.define_predicate translation
631
+ @objset.prepare_action translation
632
+ end
633
+
634
+ def migrate_state_spass(translation)
635
+ context = translation.context
636
+ translation.reserve_names context.p_names, :o do |ps, o|
637
+ translation.create_formula FOL::ForAll.new(ps, o, FOL::Equiv.new(
638
+ var.resolve_action_objset(translation, ps, o),
639
+ FOL::And.new(
640
+ translation.prev_state[ps, o],
641
+ objset.resolve_action_objset(translation, ps, o)
642
+ )
643
+ ))
644
+ end
645
+ end
646
+ end
647
+
648
+ class DSVariable < DSNode
649
+ attr_accessor :context, :pred
650
+
651
+ def action_name
652
+ "var_#{@name}"
653
+ end
654
+
655
+ # The predicate is not defined in prepare_action
656
+ # as we want the predicate to be defined only when assigning to the variable
657
+ # not when using it
658
+ # a nil check does not work because it makes the translation non-reusable
659
+ def prepare_action(translation); end
660
+ def define_predicate(translation)
661
+ @context = translation.context
662
+ @pred = translation.create_predicate action_name, context.level + 1
663
+ end
664
+
665
+ def resolve_action_objset(translation, ps, var)
666
+ return @pred[ps.first(@context.level), var]
667
+ end
668
+
669
+ def invariant_name
670
+ "invariant_var_#{@name}"
671
+ end
672
+
673
+ def resolve_invariant_objset(translation, var)
674
+ FOL::Equal.new(invariant_name, var)
675
+ end
676
+
677
+ def [](*args)
678
+ @pred[args]
679
+ end
680
+ end
681
+
682
+ class DSAllOf < DSNode
683
+ def prepare_action(translation); end
684
+
685
+ def resolve_action_objset(translation, ps, var)
686
+ return @klass[var]
687
+ end
688
+
689
+ def resolve_invariant_objset(translation, var)
690
+ return @klass[var]
691
+ end
692
+ end
693
+
694
+ class DSDereference < DSNode
695
+ def prepare_action(translation)
696
+ @objset.prepare_action translation
697
+ end
698
+
699
+ def resolve_action_objset(translation, ps, var)
700
+ translation.reserve_names :temp, :r do |temp, r|
701
+ return FOL::Exists.new(temp, r, FOL::And.new(
702
+ translation.prev_state[ps, r],
703
+ translation.prev_state[ps, temp],
704
+ @objset.resolve_action_objset(translation, ps, temp),
705
+ @relation.left_link[r, temp],
706
+ @relation.right_link[r, var]
707
+ ))
708
+ end
709
+ end
710
+
711
+ def resolve_invariant_objset(translation, var)
712
+ translation.reserve_names :temp, :r do |temp, r|
713
+ return FOL::Exists.new(temp, r, FOL::And.new(
714
+ translation.invariant_state[temp],
715
+ translation.invariant_state[r],
716
+ @objset.resolve_invariant_objset(translation, temp),
717
+ @relation.left_link[r, temp],
718
+ @relation.right_link[r, var]
719
+ ))
720
+ end
721
+ end
722
+ end
723
+
724
+ class DSSubset < DSNode
725
+ def prepare_action(translation)
726
+ @objset.prepare_action translation
727
+ end
728
+
729
+ def resolve_action_objset(translation, ps, var)
730
+ context = translation.context
731
+ pred = translation.create_predicate :subset, context.level + 1
732
+ translation.reserve_names context.p_names do |ps|
733
+ translation.create_formula FOL::ForAll.new(ps, :o,
734
+ FOL::Implies.new(pred[ps, :o], @objset.resolve_action_objset(translation, ps, :o))
735
+ )
736
+ end
737
+ return pred[ps, var]
738
+ end
739
+
740
+ def resolve_invariant_objset(translation, var)
741
+ pred = translation.create_predicate :subset, 1
742
+ translation.create_formula FOL::ForAll.new(:o,
743
+ FOL::Implies.new(pred[:o], @objset.resolve_invariant_objset(translation, :o))
744
+ )
745
+ return pred[var]
746
+ end
747
+ end
748
+
749
+ class DSUnion
750
+ def prepare_action(translation)
751
+ @objsets.each{ |objset| objset.prepare_action translation }
752
+ end
753
+
754
+ def resolve_action_objset(translation, ps, var)
755
+ FOL::Or.new(@objsets.map{ |objset| objset.resolve_action_objset translation, ps, var })
756
+ end
757
+
758
+ def resolve_invariant_objset(translation, var)
759
+ FOL::Or.new(@objsets.map{ |objset| objset.resolve_invariant_objset translation, var })
760
+ end
761
+ end
762
+
763
+ class DSOneOfObjset < DSNode
764
+ def prepare_action(translation)
765
+ context = translation.context
766
+ @objsets.each{ |objset| objset.prepare_action translation }
767
+
768
+ @predicates = @objsets.map do |objset|
769
+ translation.create_predicate :one_of_subset, context.level
770
+ end
771
+ translation.reserve_names context.p_names do |ps|
772
+ translation.create_formula FOL::ForAll.new(ps,
773
+ FOL::OneOf.new(@predicates.map{ |p| p[ps] })
774
+ )
775
+ end
776
+ end
777
+
778
+ def resolve_action_objset(translation, ps, var)
779
+ context = translation.context
780
+ subformulae = []
781
+ @objsets.length.times do |index|
782
+ subformulae << FOL::And.new(@predicates[index][ps], @objsets[index].resolve_action_objset(translation, ps, var))
783
+ end
784
+ FOL::Or.new(subformulae)
785
+ end
786
+ end
787
+
788
+ class DSEmptyObjset < DSNode
789
+ def prepare_action(translation)
790
+ end
791
+
792
+ def resolve_action_objset(translation, ps, var)
793
+ # nothing is in the objset
794
+ return false
795
+ end
796
+
797
+ def resolve_invariant_objset(translation, var)
798
+ return false
799
+ end
800
+ end
801
+
802
+ class DSOr < DSNode
803
+ def resolve_invariant_formula(translation)
804
+ FOL::Or.new(@subformulae.map{ |sub| sub.resolve_invariant_formula translation })
805
+ end
806
+ end
807
+
808
+ class DSAnd < DSNode
809
+ def resolve_invariant_formula(translation)
810
+ FOL::And.new(@subformulae.map{ |sub| sub.resolve_invariant_formula translation })
811
+ end
812
+ end
813
+
814
+ class DSEquiv < DSNode
815
+ def resolve_invariant_formula(translation)
816
+ FOL::Equiv.new(@subformulae.map{ |sub| sub.resolve_invariant_formula translation })
817
+ end
818
+ end
819
+
820
+ class DSImplies < DSNode
821
+ def resolve_invariant_formula(translation)
822
+ subformula1 = @subformula1.resolve_invariant_formula translation
823
+ subformula2 = @subformula2.resolve_invariant_formula translation
824
+ FOL::Implies.new(subformula1, subformula2)
825
+ end
826
+ end
827
+
828
+ class DSOneOf < DSNode
829
+ def prepare_action(translation)
830
+ @objset.prepare_action translation
831
+ end
832
+
833
+ def resolve_action_objset(translation, ps, var)
834
+ context = translation.context
835
+ pred = translation.create_predicate :one_of, context.level + 1
836
+ translation.gen_formula_for_unique_arg(pred, context.level)
837
+ translation.reserve_names context.p_names, :o do |subps, o|
838
+ co_in_objset = @objset.resolve_action_objset(translation, subps, o)
839
+ translation.create_formula FOL::ForAll.new(subps, FOL::Equiv.new(
840
+ FOL::Exists.new(o, FOL::And.new(translation.prev_state[subps, o], pred[subps, o])),
841
+ FOL::Exists.new(o, FOL::And.new(translation.prev_state[subps, o], co_in_objset))
842
+ ))
843
+ translation.create_formula FOL::ForAll.new(subps, o,
844
+ FOL::Implies.new(pred[subps, o], FOL::And.new(translation.prev_state[subps, o], co_in_objset))
845
+ )
846
+ end
847
+ return pred[ps, var]
848
+ end
849
+ end
850
+
851
+ class DSNot < DSNode
852
+ def resolve_invariant_formula(translation)
853
+ subformula = @subformula.resolve_invariant_formula translation
854
+ return FOL::Not.new(subformula)
855
+ end
856
+ end
857
+
858
+ class DSBoolean < DSNode
859
+ def resolve_invariant_formula(translation)
860
+ return @bool_value.resolve_spass
861
+ end
862
+ end
863
+
864
+ class DSForAll < DSNode
865
+ def resolve_invariant_formula(translation)
866
+ subformula = @subformula.resolve_invariant_formula translation
867
+ var_constraints = []
868
+ @vars.length.times do |index|
869
+ var_constraints << @objsets[index].resolve_invariant_objset(translation, @vars[index].invariant_name)
870
+ end
871
+ return FOL::ForAll.new(@vars.map{ |v| v.invariant_name }, FOL::Implies.new(
872
+ FOL::And.new(
873
+ @vars.map{ |v| translation.invariant_state[v.invariant_name] },
874
+ var_constraints
875
+ ),
876
+ subformula
877
+ )).resolve_spass
878
+ end
879
+ end
880
+
881
+ class DSExists < DSNode
882
+ def resolve_invariant_formula(translation)
883
+ subformula = @subformula.nil? ? true : @subformula.resolve_invariant_formula(translation)
884
+ subformula ||= true
885
+ return FOL::Exists.new(@vars.map{ |v| v.invariant_name }, FOL::And.new(
886
+ @vars.map{ |v| translation.invariant_state[v.invariant_name] },
887
+ @vars.map{ |v| v.type[v.invariant_name] },
888
+ @vars.map{ |v| v.resolve_invariant_objset(translation, v.invariant_name) },
889
+ subformula
890
+ )).resolve_spass
891
+ end
892
+ end
893
+
894
+ class DSEqual < DSNode
895
+ def resolve_invariant_formula(translation)
896
+ translation.reserve_names :temp do |temp|
897
+ objsets = @objsets.map{ |o| o.resolve_invariant_objset translation, temp }
898
+ return FOL::ForAll.new(temp, FOL::Implies.new(
899
+ translation.invariant_state[temp],
900
+ FOL::Equiv.new(objsets)
901
+ )).resolve_spass
902
+ end
903
+ end
904
+ end
905
+
906
+ class DSIn < DSNode
907
+ def resolve_invariant_formula(translation)
908
+ translation.reserve_names :temp do |temp|
909
+ return FOL::ForAll.new(temp, FOL::Implies.new(
910
+ translation.invariant_state[temp],
911
+ FOL::Implies.new(
912
+ @objset1.resolve_invariant_objset(translation, temp),
913
+ @objset2.resolve_invariant_objset(translation, temp)
914
+ )
915
+ ))
916
+ end
917
+ end
918
+ end
919
+
920
+ class DSEmpty < DSNode
921
+ def resolve_invariant_formula(translation)
922
+ translation.reserve_names :temp do |temp|
923
+ return FOL::ForAll.new(temp, FOL::Implies.new(
924
+ translation.invariant_state[temp],
925
+ FOL::Not.new(@objset.resolve_invariant_objset(translation, temp))
926
+ ))
927
+ end
928
+ end
929
+ end
930
+ end
931
+ end