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