adsl 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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