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.
- checksums.yaml +4 -4
- data/Gemfile +2 -20
- data/README.md +14 -21
- data/bin/adsl-verify +8 -8
- data/lib/adsl.rb +3 -0
- data/lib/adsl/adsl.rb +3 -0
- data/lib/adsl/ds/data_store_spec.rb +339 -0
- data/lib/adsl/extract/instrumenter.rb +206 -0
- data/lib/adsl/extract/meta.rb +33 -0
- data/lib/adsl/extract/rails/action_block_builder.rb +233 -0
- data/lib/adsl/extract/rails/action_instrumenter.rb +400 -0
- data/lib/adsl/extract/rails/action_runner.rb +57 -0
- data/lib/adsl/extract/rails/active_record_metaclass_generator.rb +555 -0
- data/lib/adsl/extract/rails/callback_chain_simulator.rb +135 -0
- data/lib/adsl/extract/rails/invariant_extractor.rb +48 -0
- data/lib/adsl/extract/rails/invariant_instrumenter.rb +70 -0
- data/lib/adsl/extract/rails/other_meta.rb +57 -0
- data/lib/adsl/extract/rails/rails_extractor.rb +211 -0
- data/lib/adsl/extract/rails/rails_instrumentation_test_case.rb +34 -0
- data/lib/adsl/extract/rails/rails_special_gem_instrumentation.rb +120 -0
- data/lib/adsl/extract/rails/rails_test_helper.rb +140 -0
- data/lib/adsl/extract/sexp_utils.rb +54 -0
- data/lib/adsl/fol/first_order_logic.rb +261 -0
- data/lib/adsl/parser/adsl_parser.racc +159 -0
- data/lib/{parser → adsl/parser}/adsl_parser.rex +4 -4
- data/lib/{parser → adsl/parser}/adsl_parser.rex.rb +6 -6
- data/lib/adsl/parser/adsl_parser.tab.rb +1031 -0
- data/lib/adsl/parser/ast_nodes.rb +1410 -0
- data/lib/adsl/railtie.rb +67 -0
- data/lib/adsl/spass/bin.rb +230 -0
- data/lib/{spass → adsl/spass}/ruby_extensions.rb +0 -0
- data/lib/adsl/spass/spass_ds_extensions.rb +931 -0
- data/lib/adsl/spass/spass_translator.rb +393 -0
- data/lib/adsl/spass/util.rb +13 -0
- data/lib/adsl/util/csv_hash_formatter.rb +94 -0
- data/lib/adsl/util/general.rb +228 -0
- data/lib/adsl/util/test_helper.rb +71 -0
- data/lib/adsl/verification/formula_generators.rb +231 -0
- data/lib/adsl/verification/instrumentation_filter.rb +50 -0
- data/lib/adsl/verification/invariant.rb +19 -0
- data/lib/adsl/verification/rails_verification.rb +33 -0
- data/lib/adsl/verification/utils.rb +20 -0
- data/lib/adsl/verification/verification_case.rb +13 -0
- data/test/integration/rails/rails_branch_verification_test.rb +112 -0
- data/test/integration/rails/rails_verification_test.rb +253 -0
- data/test/integration/spass/basic_translation_test.rb +563 -0
- data/test/integration/spass/control_flow_translation_test.rb +421 -0
- data/test/unit/adsl/ds/data_store_spec_test.rb +54 -0
- data/test/unit/adsl/extract/instrumenter_test.rb +103 -0
- data/test/unit/adsl/extract/meta_test.rb +142 -0
- data/test/unit/adsl/extract/rails/action_block_builder_test.rb +178 -0
- data/test/unit/adsl/extract/rails/action_instrumenter_test.rb +68 -0
- data/test/unit/adsl/extract/rails/active_record_metaclass_generator_test.rb +336 -0
- data/test/unit/adsl/extract/rails/callback_chain_simulator_test.rb +76 -0
- data/test/unit/adsl/extract/rails/invariant_extractor_test.rb +92 -0
- data/test/unit/adsl/extract/rails/rails_extractor_test.rb +1380 -0
- data/test/unit/adsl/extract/rails/rails_test_helper_test.rb +25 -0
- data/test/unit/adsl/extract/sexp_utils_test.rb +100 -0
- data/test/unit/adsl/fol/first_order_logic_test.rb +227 -0
- data/test/unit/adsl/parser/action_parser_test.rb +1040 -0
- data/test/unit/adsl/parser/ast_nodes_test.rb +359 -0
- data/test/unit/adsl/parser/class_parser_test.rb +288 -0
- data/test/unit/adsl/parser/general_parser_test.rb +67 -0
- data/test/unit/adsl/parser/invariant_parser_test.rb +432 -0
- data/test/unit/adsl/parser/parser_util_test.rb +126 -0
- data/test/unit/adsl/spass/bin_test.rb +65 -0
- data/test/unit/adsl/spass/ruby_extensions_test.rb +39 -0
- data/test/unit/adsl/spass/spass_ds_extensions_test.rb +7 -0
- data/test/unit/adsl/spass/spass_translator_test.rb +342 -0
- data/test/unit/adsl/util/csv_hash_formatter_test.rb +68 -0
- data/test/unit/adsl/util/general_test.rb +303 -0
- data/test/unit/adsl/util/test_helper_test.rb +120 -0
- data/test/unit/adsl/verification/formula_generators_test.rb +200 -0
- data/test/unit/adsl/verification/instrumentation_filter_test.rb +39 -0
- data/test/unit/adsl/verification/utils_test.rb +39 -0
- data/test/unit/adsl/verification/verification_case_test.rb +8 -0
- metadata +229 -29
- data/lib/ds/data_store_spec.rb +0 -292
- data/lib/fol/first_order_logic.rb +0 -260
- data/lib/parser/adsl_ast.rb +0 -779
- data/lib/parser/adsl_parser.racc +0 -151
- data/lib/parser/adsl_parser.tab.rb +0 -976
- data/lib/parser/dsdl_parser.rex.rb +0 -196
- data/lib/parser/dsdl_parser.tab.rb +0 -976
- data/lib/spass/bin.rb +0 -164
- data/lib/spass/spass_ds_extensions.rb +0 -870
- data/lib/spass/spass_translator.rb +0 -388
- data/lib/spass/util.rb +0 -11
- data/lib/util/csv_hash_formatter.rb +0 -47
- data/lib/util/test_helper.rb +0 -33
- data/lib/util/util.rb +0 -114
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'thread'
|
|
4
|
+
require 'active_support'
|
|
5
|
+
|
|
6
|
+
class String
|
|
7
|
+
def increment_suffix
|
|
8
|
+
suffix = scan(/_(\d+)$/).last
|
|
9
|
+
if suffix.nil?
|
|
10
|
+
return self + "_2"
|
|
11
|
+
else
|
|
12
|
+
suffix = suffix.first
|
|
13
|
+
return self[0, self.length - suffix.length] + (suffix.to_i + 1).to_s
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# for lolz
|
|
18
|
+
def dyslexicize
|
|
19
|
+
gsub(/(\w)(\w+)(\w)/) { |match| ([$1] + $2.chars.to_a.shuffle + [$3]).join('') }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def adsl_indent
|
|
23
|
+
indented = " " + gsub("\n", "\n ")
|
|
24
|
+
(/ $/ =~ indented) ? indented[0..-3] : indented
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Time
|
|
29
|
+
def self.time_execution
|
|
30
|
+
pre = Time.now
|
|
31
|
+
yield
|
|
32
|
+
((Time.now - pre)*1000).to_i
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Array
|
|
37
|
+
def worklist_each
|
|
38
|
+
changed = true
|
|
39
|
+
until empty? or not changed
|
|
40
|
+
changed = false
|
|
41
|
+
length.times do
|
|
42
|
+
task = self.shift
|
|
43
|
+
new_value = yield task
|
|
44
|
+
self << new_value if new_value
|
|
45
|
+
changed = true if task != new_value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def adsl_indent
|
|
51
|
+
join("").adsl_indent
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def select_reject
|
|
55
|
+
arr1 = []
|
|
56
|
+
arr2 = []
|
|
57
|
+
self.each do |e|
|
|
58
|
+
if yield e
|
|
59
|
+
arr1 << e
|
|
60
|
+
else
|
|
61
|
+
arr2 << e
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
return arr1, arr2
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def set_to(array)
|
|
68
|
+
self.clear
|
|
69
|
+
array.each{ |e| self << e }
|
|
70
|
+
self
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class Symbol
|
|
75
|
+
def dup; self; end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class NilClass
|
|
79
|
+
def dup; self; end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class Fixnum
|
|
83
|
+
def dup; self; end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class TrueClass
|
|
87
|
+
def dup; self; end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class FalseClass
|
|
91
|
+
def dup; self; end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class Module
|
|
95
|
+
def parent_module
|
|
96
|
+
name.split('::')[0..-2].join('::').constantize
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def lookup_const(const)
|
|
100
|
+
lookup_container = self
|
|
101
|
+
const.to_s.split('::').each do |portion|
|
|
102
|
+
portion = 'Object' if portion.empty?
|
|
103
|
+
return nil unless lookup_container.const_defined? portion
|
|
104
|
+
lookup_container = lookup_container.const_get portion
|
|
105
|
+
end
|
|
106
|
+
lookup_container
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def lookup_or_create_module(name)
|
|
110
|
+
lookup_container = self
|
|
111
|
+
name.to_s.split('::').each do |portion|
|
|
112
|
+
portion = 'Object' if portion.empty?
|
|
113
|
+
unless lookup_container.const_defined? portion
|
|
114
|
+
new_module = Module.new
|
|
115
|
+
lookup_container.const_set portion, new_module
|
|
116
|
+
end
|
|
117
|
+
lookup_container = lookup_container.const_get portion
|
|
118
|
+
end
|
|
119
|
+
lookup_container
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def lookup_or_create_class(name, superclass)
|
|
123
|
+
already_defined = lookup_const name
|
|
124
|
+
return already_defined unless already_defined.nil?
|
|
125
|
+
|
|
126
|
+
container_name = name.match(/^(.*)::\w+$/) ? $1 : 'Object'
|
|
127
|
+
container = lookup_or_create_module container_name
|
|
128
|
+
new_class = Class.new(superclass)
|
|
129
|
+
container.const_set name.to_s.split('::').last, new_class
|
|
130
|
+
new_class
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def container_for(*fields, &block)
|
|
134
|
+
all_fields = Set.new(fields)
|
|
135
|
+
if respond_to? :container_for_fields
|
|
136
|
+
prev_fields = send :container_for_fields
|
|
137
|
+
all_fields.merge prev_fields
|
|
138
|
+
end
|
|
139
|
+
singleton_class.send :define_method, :container_for_fields, lambda{ all_fields }
|
|
140
|
+
|
|
141
|
+
attr_accessor *fields
|
|
142
|
+
|
|
143
|
+
send :define_method, :initialize do |*options|
|
|
144
|
+
options = options.empty? ? {} : options[0]
|
|
145
|
+
options ||= {}
|
|
146
|
+
options_trimmed = Hash[options]
|
|
147
|
+
|
|
148
|
+
all_fields.each do |field|
|
|
149
|
+
instance_variable_set "@#{field}".to_sym, options_trimmed.delete(field.to_sym)
|
|
150
|
+
end
|
|
151
|
+
if block
|
|
152
|
+
instance_eval &block
|
|
153
|
+
elsif not options_trimmed.empty?
|
|
154
|
+
raise ArgumentError, "Undefined fields mentioned in initializer of #{self.class}: #{options_trimmed.keys.map{ |key| ":#{key.to_s}"}.join(", ")}" + "\n[#{all_fields.to_a.join ' ' }]"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
send :define_method, :recursively_gather do |method|
|
|
159
|
+
to_inspect = [self]
|
|
160
|
+
inspected = Set[]
|
|
161
|
+
while not to_inspect.empty?
|
|
162
|
+
elem = to_inspect.pop
|
|
163
|
+
inspected << elem
|
|
164
|
+
if elem.class.respond_to? :container_for_fields
|
|
165
|
+
elem.class.container_for_fields.each do |field|
|
|
166
|
+
field_val = elem.send(field)
|
|
167
|
+
next if field_val.nil?
|
|
168
|
+
[field_val].flatten.each do |subval|
|
|
169
|
+
to_inspect << subval unless inspected.include?(subval)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
result = Set[]
|
|
175
|
+
inspected.each do |val|
|
|
176
|
+
result = result + [val.send(method)].flatten if val.class.method_defined?(method)
|
|
177
|
+
end
|
|
178
|
+
result.delete_if{ |a| a.nil? }.flatten
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
module Kernel
|
|
184
|
+
# returns stdout of the process that terminates first
|
|
185
|
+
# not completely thread safe; cannot be with 1.8.7
|
|
186
|
+
def process_race(*commands)
|
|
187
|
+
parent_thread = Thread.current
|
|
188
|
+
mutex = Mutex.new
|
|
189
|
+
children_threads = []
|
|
190
|
+
spawned_pids = []
|
|
191
|
+
result = nil
|
|
192
|
+
mutex.synchronize do
|
|
193
|
+
commands.each do |command|
|
|
194
|
+
children_threads << Thread.new do
|
|
195
|
+
begin
|
|
196
|
+
sleep 0.1
|
|
197
|
+
pipe = IO.popen command, 'r'
|
|
198
|
+
spawned_pids << pipe.pid
|
|
199
|
+
output = pipe.read
|
|
200
|
+
mutex.synchronize do
|
|
201
|
+
result = output if result.nil?
|
|
202
|
+
parent_thread.run
|
|
203
|
+
end
|
|
204
|
+
rescue => e
|
|
205
|
+
parent_thread.raise e unless e.message == 'die!'
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
Thread.stop
|
|
211
|
+
return result
|
|
212
|
+
ensure
|
|
213
|
+
children_threads.each do |child|
|
|
214
|
+
child.raise 'die!'
|
|
215
|
+
end
|
|
216
|
+
spawned_pids.each do |pid|
|
|
217
|
+
Process.kill 'HUP', pid
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def until_no_change(object)
|
|
222
|
+
loop do
|
|
223
|
+
old_object = object
|
|
224
|
+
object = yield object
|
|
225
|
+
return object if old_object == object
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'adsl/parser/adsl_parser.tab'
|
|
3
|
+
require 'adsl/spass/bin'
|
|
4
|
+
require 'adsl/spass/spass_ds_extensions'
|
|
5
|
+
require 'adsl/spass/util'
|
|
6
|
+
require 'adsl/util/general'
|
|
7
|
+
|
|
8
|
+
class Test::Unit::TestCase
|
|
9
|
+
include ADSL::Spass::Bin
|
|
10
|
+
include ADSL::Spass::Util
|
|
11
|
+
|
|
12
|
+
SPASS_TIMEOUT = 5
|
|
13
|
+
|
|
14
|
+
def adsl_assert(expected_result, input, options={})
|
|
15
|
+
ds_spec = ADSL::Parser::ADSLParser.new.parse input
|
|
16
|
+
raise "Exactly one action required in ADSL" if ds_spec.actions.length != 1
|
|
17
|
+
action_name = ds_spec.actions.first.name
|
|
18
|
+
spass = ds_spec.translate_action(action_name)
|
|
19
|
+
spass = replace_conjecture spass, options[:conjecture] if options.include? :conjecture
|
|
20
|
+
result = exec_spass(spass, options[:timeout] || SPASS_TIMEOUT)
|
|
21
|
+
if result == :inconclusive
|
|
22
|
+
puts "inconclusive result on testcase #{self.class.name}.#{method_name}"
|
|
23
|
+
else
|
|
24
|
+
assert_equal expected_result, result
|
|
25
|
+
end
|
|
26
|
+
rescue Exception => e
|
|
27
|
+
puts spass unless spass.nil?
|
|
28
|
+
raise e
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def spass_assert(expected_result, input, timeout = SPASS_TIMEOUT)
|
|
32
|
+
adsl_assert expected_result, input, :timeout => timeout
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def unload_class(*classes)
|
|
36
|
+
classes.each do |klass_name|
|
|
37
|
+
const = self.class.lookup_const klass_name
|
|
38
|
+
next if const.nil?
|
|
39
|
+
const.parent_module.send :remove_const, const.name.split('::').last
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def class_defined?(*classes)
|
|
44
|
+
classes.each do |klass_name|
|
|
45
|
+
return true unless self.class.lookup_const(klass_name).nil?
|
|
46
|
+
end
|
|
47
|
+
return false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def in_temp_file(content)
|
|
51
|
+
file = Tempfile.new('test_util')
|
|
52
|
+
file.write content
|
|
53
|
+
file.close
|
|
54
|
+
yield file.path
|
|
55
|
+
ensure
|
|
56
|
+
file.unlink unless file.nil?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def assert_set_equal(expected, actual, failure_msg = nil)
|
|
60
|
+
expected.each do |elem|
|
|
61
|
+
assert_block failure_msg || "Actual collection does not contain `#{elem}'" do
|
|
62
|
+
actual.include? elem
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
actual.each do |elem|
|
|
66
|
+
assert_block failure_msg || "Expected collection does not contain `#{elem}'" do
|
|
67
|
+
expected.include? elem
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
require 'adsl/verification/utils'
|
|
2
|
+
require 'adsl/parser/ast_nodes'
|
|
3
|
+
require 'adsl/util/general'
|
|
4
|
+
|
|
5
|
+
module ADSL
|
|
6
|
+
module Verification
|
|
7
|
+
|
|
8
|
+
# forward declaration
|
|
9
|
+
class FormulaBuilder; end
|
|
10
|
+
|
|
11
|
+
module FormulaGenerators
|
|
12
|
+
include Utils
|
|
13
|
+
include ADSL::Parser
|
|
14
|
+
|
|
15
|
+
def in_formula_builder
|
|
16
|
+
formula_builder = nil
|
|
17
|
+
if self.is_a? ::ADSL::Verification::FormulaBuilder
|
|
18
|
+
formula_builder = self
|
|
19
|
+
else
|
|
20
|
+
formula_builder = ::ADSL::Verification::FormulaBuilder.new
|
|
21
|
+
formula_builder.adsl_stack << self.adsl_ast if self.respond_to? :adsl_ast
|
|
22
|
+
end
|
|
23
|
+
yield formula_builder
|
|
24
|
+
formula_builder
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def handle_quantifier(quantifier, adsl_ast_node_klass, arg_types, block)
|
|
28
|
+
raise "A block needs to be passed to quantifiers" if block.nil?
|
|
29
|
+
raise "At least some variables need to be given to the block" if block.parameters.empty?
|
|
30
|
+
in_formula_builder do |fb|
|
|
31
|
+
|
|
32
|
+
param_types = {}
|
|
33
|
+
block.parameters.each do |param|
|
|
34
|
+
classname = infer_classname_from_varname param[1]
|
|
35
|
+
klass = Object.lookup_const classname
|
|
36
|
+
param_types[param[1].to_sym] = klass
|
|
37
|
+
end
|
|
38
|
+
arg_types.each do |explicit_name, explicit_type|
|
|
39
|
+
classname = classname_for_classname explicit_type
|
|
40
|
+
klass = Object.lookup_const classname
|
|
41
|
+
raise "Unknown class #{explicit_type} for parameter #{explicit_name}" if klass.nil?
|
|
42
|
+
param_types[explicit_name.to_sym] = klass
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
param_types.each do |name, klass|
|
|
46
|
+
raise "Unknown klass for variable `#{name}' in #{quantifier} quantifier" if klass.nil?
|
|
47
|
+
raise "Class #{klass.name} is not instrumented" unless klass.respond_to? :adsl_ast
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
vars_and_objsets = block.parameters.map{ |param|
|
|
51
|
+
[
|
|
52
|
+
t(param[1].to_s),
|
|
53
|
+
param_types[param[1].to_sym].all.adsl_ast
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
subformula = block.(*block.parameters.map do |param|
|
|
57
|
+
param_types[param[1].to_sym].new :adsl_ast => ASTVariable.new(:var_name => t(param[1]))
|
|
58
|
+
end)
|
|
59
|
+
subformula = true if subformula.nil?
|
|
60
|
+
raise "Invalid formula returned by block in `#{quantifier}'" unless subformula.respond_to? :adsl_ast
|
|
61
|
+
fb.adsl_stack << adsl_ast_node_klass.new(:vars => vars_and_objsets, :subformula => subformula.adsl_ast)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def forall(arg_types = {}, &block)
|
|
66
|
+
handle_quantifier :forall, ASTForAll, arg_types, block
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def exists(arg_types = {}, &block)
|
|
70
|
+
handle_quantifier :exists, ASTExists, arg_types, block
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def not(param = nil)
|
|
74
|
+
in_formula_builder do |fb|
|
|
75
|
+
if param.nil?
|
|
76
|
+
fb.adsl_stack << :not
|
|
77
|
+
else
|
|
78
|
+
fb.adsl_stack << ASTNot.new(:subformula => param.adsl_ast)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
alias_method :neg, :not
|
|
83
|
+
|
|
84
|
+
def true
|
|
85
|
+
in_formula_builder do |fb|
|
|
86
|
+
fb.adsl_stack << true.adsl_ast
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def false
|
|
91
|
+
in_formula_builder do |fb|
|
|
92
|
+
fb.adsl_stack << false.adsl_ast
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def [](formula)
|
|
97
|
+
in_formula_builder do |fb|
|
|
98
|
+
formula = formula.adsl_ast if formula.respond_to? :adsl_ast
|
|
99
|
+
fb.adsl_stack << formula
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def binary_op_with_any_number_of_params(op, klass, params)
|
|
104
|
+
in_formula_builder do |fb|
|
|
105
|
+
if params.empty?
|
|
106
|
+
fb.adsl_stack << op
|
|
107
|
+
else
|
|
108
|
+
params.each do |param|
|
|
109
|
+
raise "Invalid formula `#{param}' in `#{op}' parameter list" unless param.respond_to? :adsl_ast
|
|
110
|
+
end
|
|
111
|
+
fb.adsl_stack << klass.new(:subformulae => params.map(&:adsl_ast))
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def binary_op(op, klass, params)
|
|
117
|
+
raise "`#{op}' takes two parameters or none at all" unless params.empty? or params.length == 2
|
|
118
|
+
in_formula_builder do |fb|
|
|
119
|
+
if params.empty?
|
|
120
|
+
fb.adsl_stack << op
|
|
121
|
+
else
|
|
122
|
+
params.each do |param|
|
|
123
|
+
raise "Invalid formula `#{param}' in `#{op}' parameter list" unless param.respond_to? :adsl_ast
|
|
124
|
+
end
|
|
125
|
+
fb.adsl_stack << klass.new(:subformula1 => params.first.adsl_ast, :subformula2 => params.last.adsl_ast)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def and(*params)
|
|
131
|
+
binary_op_with_any_number_of_params :and, ASTAnd, params
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def or(*params)
|
|
135
|
+
binary_op_with_any_number_of_params :or, ASTOr, params
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def equiv(*params)
|
|
139
|
+
binary_op_with_any_number_of_params :equiv, ASTEquiv, params
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def implies(*params)
|
|
143
|
+
binary_op :implies, ASTImplies, params
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class FormulaBuilder
|
|
148
|
+
include FormulaGenerators
|
|
149
|
+
|
|
150
|
+
attr_reader :adsl_stack
|
|
151
|
+
|
|
152
|
+
def initialize(component = nil)
|
|
153
|
+
@adsl_stack = []
|
|
154
|
+
@adsl_stack << component unless component.nil?
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def handle_unary_operator(elements, operator)
|
|
158
|
+
until (index = elements.find_index(operator)).nil?
|
|
159
|
+
raise "Unary operator `#{operator}' not prefix-called" if index == elements.length-1
|
|
160
|
+
arg = elements[index+1]
|
|
161
|
+
raise "`#{arg}', used by operator `#{operator}', is not a formula" unless arg.is_a? ASTNode
|
|
162
|
+
result = yield arg
|
|
163
|
+
elements.delete_at index
|
|
164
|
+
elements[index] = result
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def handle_binary_operator(elements, operator)
|
|
169
|
+
until (index = elements.find_index(operator)).nil?
|
|
170
|
+
raise "Binary operator `#{operator}' not infix-called" if index == 0 or index == elements.length-1
|
|
171
|
+
args = [elements[index-1], elements[index+1]]
|
|
172
|
+
args.each do |arg|
|
|
173
|
+
raise "`#{arg}', used by operator `#{operator}', is not a formula" unless arg.is_a? ASTNode
|
|
174
|
+
end
|
|
175
|
+
result = yield *args
|
|
176
|
+
elements.delete_at index - 1
|
|
177
|
+
elements.delete_at index - 1
|
|
178
|
+
elements[index - 1] = result
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def gather_adsl_asts
|
|
183
|
+
elements = @adsl_stack.clone
|
|
184
|
+
handle_unary_operator elements, :not do |formula|
|
|
185
|
+
ASTNot.new(:subformula => formula)
|
|
186
|
+
end
|
|
187
|
+
handle_binary_operator elements, :and do |formula1, formula2|
|
|
188
|
+
ASTAnd.new(:subformulae => [formula1, formula2])
|
|
189
|
+
end
|
|
190
|
+
handle_binary_operator elements, :or do |formula1, formula2|
|
|
191
|
+
ASTOr.new(:subformulae => [formula1, formula2])
|
|
192
|
+
end
|
|
193
|
+
handle_binary_operator elements, :implies do |formula1, formula2|
|
|
194
|
+
ASTImplies.new(:subformula1 => formula1, :subformula2 => formula2)
|
|
195
|
+
end
|
|
196
|
+
handle_binary_operator elements, :equiv do |formula1, formula2|
|
|
197
|
+
ASTEquiv.new(:subformula1 => formula1, :subformula2 => formula2)
|
|
198
|
+
end
|
|
199
|
+
elements
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def adsl_ast
|
|
203
|
+
elements = gather_adsl_asts
|
|
204
|
+
if elements.length != 1
|
|
205
|
+
raise "Invalid formula/operator stack state [#{ elements.map{ |e| e.respond_to?(:to_adsl) ? e.to_adsl : e }.join(', ') }]"
|
|
206
|
+
end
|
|
207
|
+
elements.first
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
class TrueClass
|
|
214
|
+
include ADSL::Verification::FormulaGenerators
|
|
215
|
+
|
|
216
|
+
def adsl_ast
|
|
217
|
+
ASTBoolean.new(:bool_value => self)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
class FalseClass
|
|
222
|
+
include ADSL::Verification::FormulaGenerators
|
|
223
|
+
|
|
224
|
+
def adsl_ast
|
|
225
|
+
ASTBoolean.new(:bool_value => self)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
class ADSL::Parser::ASTNode
|
|
230
|
+
include ADSL::Verification::FormulaGenerators
|
|
231
|
+
end
|