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.
@@ -0,0 +1,261 @@
1
+ require 'rubygems'
2
+ require 'active_support/all'
3
+ require 'util/util'
4
+
5
+ class String
6
+ def resolve_spass
7
+ self
8
+ end
9
+
10
+ def split_by_zero_level_comma
11
+ parts = []
12
+ sequence_beginning_index = 0
13
+ index = 0
14
+ paren_level = 0
15
+ while index < length
16
+ if self[index, 1] == '('
17
+ paren_level += 1
18
+ elsif self[index, 1] == ')'
19
+ paren_level -= 1
20
+ raise ArgumentError, 'Unmatching parenthesis' if paren_level < 0
21
+ elsif self[index, 1] == ',' and paren_level == 0
22
+ parts << self[sequence_beginning_index, index - sequence_beginning_index].strip
23
+ sequence_beginning_index = index + 1
24
+ end
25
+ index += 1
26
+ end
27
+ parts << self[sequence_beginning_index, length - sequence_beginning_index].strip
28
+ parts
29
+ end
30
+ end
31
+
32
+ class Symbol
33
+ def resolve_spass
34
+ to_s
35
+ end
36
+ end
37
+
38
+ class TrueClass
39
+ def resolve_spass
40
+ "true"
41
+ end
42
+ end
43
+
44
+ class FalseClass
45
+ def resolve_spass
46
+ "false"
47
+ end
48
+ end
49
+
50
+ module FOL
51
+ class Not
52
+ def initialize(*formulae)
53
+ @formulae = formulae.flatten
54
+ raise ArgumentError, "At least one subformula required" if @formulae.empty?
55
+ end
56
+
57
+ def resolve_spass
58
+ children = @formulae.map{ |obj| obj.resolve_spass }
59
+ children.delete_if{ |a| a == 'false' }
60
+ return 'false' if children.include? 'true'
61
+ return And.new(children.map{ |child| child.match('\Anot\((.*)\)\z') ? $1 : "not(#{child})" }).resolve_spass
62
+ end
63
+ end
64
+
65
+
66
+ class And
67
+ attr_reader :objs
68
+
69
+ def initialize(*objs)
70
+ @objs = objs.flatten
71
+ end
72
+
73
+ def resolve_spass
74
+ children = @objs.map{ |child| child.resolve_spass }
75
+ children = children.map{ |child| child.match('\Aand\((.*)\)\z') ? $1.split_by_zero_level_comma : child }.flatten
76
+ children.delete_if{ |a| a == 'true' }
77
+ return 'false' if children.include? 'false'
78
+ return 'true' if children.empty?
79
+ return children.first if children.length == 1
80
+ return "and(#{children.join(', ')})"
81
+ end
82
+ end
83
+
84
+ class Or
85
+ attr_reader :objs
86
+
87
+ def initialize(*objs)
88
+ @objs = objs.flatten
89
+ end
90
+
91
+ def resolve_spass
92
+ children = @objs.map{ |child| child.resolve_spass }
93
+ children = children.map{ |child| child.match('\Aor\((.*)\)\z') ? $1.split_by_zero_level_comma : child }.flatten
94
+ children.delete_if{ |a| a == 'false' }
95
+ return 'true' if children.include? 'true'
96
+ return 'false' if children.empty?
97
+ return children.first if children.length == 1
98
+ return "or(#{children.join(', ')})"
99
+ end
100
+ end
101
+
102
+ class ForAll
103
+ def initialize(*params)
104
+ params = params.flatten
105
+ raise ArgumentError, "At least a formula required" if params.length < 1
106
+ @args = params.first(params.length - 1)
107
+ @formula = params.last
108
+ end
109
+
110
+ def resolve_spass
111
+ args = @args.map{ |obj| obj.resolve_spass }
112
+ formula = @formula.resolve_spass
113
+ return formula if args.empty?
114
+ return 'true' if formula == 'true'
115
+ return 'false' if formula == 'false'
116
+ "forall( [#{args.join(', ')}], #{formula})"
117
+ end
118
+ end
119
+
120
+ class Exists
121
+ def initialize(*params)
122
+ params = params.flatten
123
+ raise ArgumentError, "At least a formula required" if params.length < 1
124
+ @args = params.first(params.length - 1)
125
+ @formula = params.last
126
+ end
127
+
128
+ def resolve_spass
129
+ args = @args.map{ |obj| obj.resolve_spass }
130
+ formula = @formula.resolve_spass
131
+ return formula if args.empty?
132
+ return 'true' if formula == 'true'
133
+ return 'false' if formula == 'false'
134
+ "exists( [#{args.join(', ')}], #{formula})"
135
+ end
136
+ end
137
+
138
+ class Equal
139
+ def initialize(*subformulae)
140
+ @subformulae = subformulae.flatten
141
+ raise ArgumentError, "At least two subformulae required" if @subformulae.length < 2
142
+ end
143
+
144
+ def resolve_spass
145
+ return @subformulae.first.resolve_spass if @subformulae.length == 1
146
+ combinations = []
147
+ (@subformulae.length-1).times do |index|
148
+ combinations << "equal(#{@subformulae[index].resolve_spass}, #{@subformulae[index+1].resolve_spass})"
149
+ end
150
+ return And.new(combinations).resolve_spass
151
+ end
152
+ end
153
+
154
+ class Equiv
155
+ def initialize(*subformulae)
156
+ @subformulae = subformulae.flatten
157
+ raise ArgumentError, "At least two subformulae required" if @subformulae.length < 2
158
+ end
159
+
160
+ def resolve_spass
161
+ subformulae = @subformulae.map{ |sub| sub.resolve_spass }
162
+ return subformulae.first if subformulae.length == 1
163
+ return And.new(subformulae).resolve_spass if subformulae.include? 'true'
164
+ return Not.new(subformulae).resolve_spass if subformulae.include? 'false'
165
+ combinations = []
166
+ (subformulae.length-1).times do |index|
167
+ combinations << "equiv(#{subformulae[index]}, #{subformulae[index+1]})"
168
+ end
169
+ return And.new(combinations).resolve_spass
170
+ end
171
+ end
172
+
173
+ class Implies
174
+ def initialize(from, to)
175
+ @from = from
176
+ @to = to
177
+ end
178
+
179
+ def resolve_spass
180
+ from = @from.resolve_spass
181
+ to = @to.resolve_spass
182
+ return to if from == 'true'
183
+ return 'true' if from == 'false'
184
+ return Not.new(from).resolve_spass if to == 'false'
185
+ return 'true' if to == 'true'
186
+ return "implies(#{from}, #{to})"
187
+ end
188
+ end
189
+
190
+ class OneOf
191
+ def initialize(*formulae)
192
+ @formulae = formulae.flatten
193
+ end
194
+
195
+ def resolve_spass
196
+ return 'false' if @formulae.empty?
197
+ return @formulae.first.resolve_spass if @formulae.length == 1
198
+ return Equiv.new(Not.new(@formulae.first), @formulae.last).resolve_spass if @formulae.length == 2
199
+
200
+ substatements = []
201
+ @formulae.length.times do |i|
202
+ formulae_without_i = @formulae.first(i) + @formulae.last(@formulae.length - 1 - i)
203
+ substatements << Implies.new(@formulae[i], Not.new(formulae_without_i))
204
+ end
205
+ And.new(Or.new(@formulae), substatements).resolve_spass
206
+ end
207
+ end
208
+
209
+ class IfThenElse
210
+ def initialize(iif, tthen, eelse)
211
+ @iif = iif
212
+ @tthen = tthen
213
+ @eelse = eelse
214
+ end
215
+
216
+ def resolve_spass
217
+ And.new(Implies.new(@iif, @tthen), Implies.new(Not.new(@iif), @eelse)).resolve_spass
218
+ end
219
+ end
220
+
221
+ class IfThenElseEq
222
+ def initialize(iif, tthen, eelse)
223
+ @iif = iif
224
+ @tthen = tthen
225
+ @eelse = eelse
226
+ end
227
+
228
+ def resolve_spass
229
+ And.new(Equiv.new(@iif, @tthen), Equiv.new(Not.new(@iif), @eelse)).resolve_spass
230
+ end
231
+ end
232
+
233
+ class PairwiseEqual
234
+ def initialize(*list)
235
+ list = list.flatten
236
+ @list1 = list.first((list.length/2.0).ceil)
237
+ @list2 = list.last((list.length/2.0).floor)
238
+ raise ArgumentError, "Lists not of equal length: [#{@list1.join(", ")}], [#{@list2.join(", ")}]" if @list1.length != @list2.length
239
+ end
240
+
241
+ def resolve_spass
242
+ equalities = []
243
+ @list1.length.times do |i|
244
+ equalities << Equal.new(@list1[i], @list2[i])
245
+ end
246
+ return And.new(equalities).resolve_spass
247
+ end
248
+ end
249
+
250
+ # define a function for each of the above classes, starting with underscore and underscored* afterwards
251
+ # *see: http://api.rubyonrails.org/v2.3.8/classes/ActiveSupport/CoreExtensions/String/Inflections.html
252
+ self.constants.each do |klass_name|
253
+ instance_eval do
254
+ klass = FOL.const_get(klass_name)
255
+ self.send :define_method, "_#{klass_name.underscore}".to_sym do |*args|
256
+ klass.new(*args).resolve_spass
257
+ end
258
+ end
259
+ end
260
+ end
261
+
@@ -0,0 +1,780 @@
1
+ require 'ds/data_store_spec'
2
+ require 'rubygems'
3
+ require 'active_support'
4
+ require 'util/util'
5
+ require 'pp'
6
+ require 'set'
7
+
8
+ module ADSL
9
+
10
+ class ADSLNode
11
+ def self.node_type(*fields)
12
+ container_for *fields
13
+ container_for :lineno
14
+ end
15
+ end
16
+
17
+ class ADSLError < StandardError; end
18
+
19
+ class ADSLDummyObjset < ADSLNode
20
+ node_type :type
21
+
22
+ def typecheck_and_resolve(context)
23
+ self
24
+ end
25
+ end
26
+
27
+ class ADSLSpec < ADSLNode
28
+ node_type :classes, :actions, :invariants
29
+
30
+ def typecheck_and_resolve
31
+ context = ADSLTypecheckResolveContext.new
32
+
33
+ # make sure class names are unique
34
+ @classes.each do |class_node|
35
+ if context.classes.include? class_node.name.text
36
+ raise ADSLError, "Duplicate class name '#{class_node.name.text}' on line #{class_node.name.lineno} (first definition on line #{context.classes[class_node.name.text][0].name.lineno}"
37
+ end
38
+ klass = DS::DSClass.new :name => class_node.name.text
39
+ context.classes[klass.name] = [class_node, klass]
40
+ end
41
+
42
+ # make sure the parent classes are declared properly and that the inheritance graph is non-cyclic
43
+ parents = Hash.new{}
44
+ context.classes.values.select{ |v| v[0].parent_name }.each do |class_node, klass|
45
+ parent_node, parent = context.classes[class_node.parent_name.text]
46
+ raise ADSLError, "Unknown parent class name #{class_node.parent_name.text} for class #{class_node.name.text} on line #{class_node.parent_name}" if parent.nil?
47
+ klass.parent = parent
48
+
49
+ parents[klass] = parent
50
+ parent_chain = [klass]
51
+ while parent != nil do
52
+ if parent_chain.include? parent
53
+ cyclic_chain = parent_chain.slice(parent_chain.index(parent), parent_chain.length) + [parent]
54
+ raise ADSLError, "Cyclic inheritance detected: #{cyclic_chain.map{ |c| c.name }.join ' -> '}"
55
+ end
56
+ parent_chain << parent
57
+ parent = parents[parent]
58
+ end
59
+ end
60
+
61
+ # make sure relations are valid and refer to existing classes
62
+ context.classes.values.each do |class_node, klass|
63
+ class_node.relations.each do |rel_node|
64
+ iter = klass
65
+ while iter != nil
66
+ if context.relations[iter.name].include? rel_node.name.text
67
+ raise ADSLError, "Duplicate relation name '#{class_node.name.text}' under class '#{klass.name}' on line #{rel_node.lineno} (first definition on line #{context.relations[iter.name][rel_node.name.text][0].lineno}"
68
+ end
69
+ iter = iter.parent
70
+ end
71
+ rel = DS::DSRelation.new :name => rel_node.name.text, :from_class => klass
72
+ context.relations[klass.name][rel.name] = [rel_node, rel]
73
+ end
74
+ end
75
+
76
+ # now that classes and rels are initialized, check them
77
+ @classes.each do |class_node|
78
+ class_node.typecheck_and_resolve context
79
+ end
80
+
81
+ @actions.each do |action_node|
82
+ action_node.typecheck_and_resolve context
83
+ end
84
+
85
+ # make sure invariants have unique names; add names to unnamed invariants
86
+ names = Set.new
87
+ @invariants.each do |invariant_node|
88
+ invariant = invariant_node.typecheck_and_resolve context
89
+ if invariant.name && names.include?(invariant.name)
90
+ raise ADSLError, "Duplicate invariant name #{invariant.name} on line #{invariant_node.lineno}"
91
+ end
92
+ name = invariant.name || "unnamed_line_#{invariant_node.lineno}"
93
+ while names.include? name
94
+ name = name.increment_suffix
95
+ end
96
+ invariant.name = name
97
+ context.invariants << invariant
98
+ names << name
99
+ end
100
+
101
+ @invariants.each do |invariant_node|
102
+ invariant = invariant_node.typecheck_and_resolve context
103
+ end
104
+
105
+ DS::DSSpec.new(
106
+ :classes => context.classes.map{ |a, b| b[1] },
107
+ :actions => context.actions.map{ |a, b| b[1] },
108
+ :invariants => context.invariants.dup
109
+ )
110
+ end
111
+ end
112
+
113
+ class ADSLClass < ADSLNode
114
+ node_type :name, :parent_name, :relations
115
+
116
+ def typecheck_and_resolve(context)
117
+ klass = context.classes[@name.text][1]
118
+ @relations.each do |rel_node|
119
+ rel = context.relations[@name.text][rel_node.name.text][1]
120
+ klass.relations << rel
121
+
122
+ if rel_node.cardinality[0] > rel_node.cardinality[1]
123
+ raise ADSLError, "Invalid cardinality of relation #{klass.name}.#{rel_node.name.text} on line #{rel_node.cardinality[2]}: minimum cardinality #{rel_node.cardinality[0]} must not be greater than the maximum cardinality #{rel_node.cardinality[1]}"
124
+ end
125
+ if rel_node.cardinality[1] == 0
126
+ raise ADSLError, "Invalid cardinality of relation #{klass.name}.#{rel_node.name.text} on line #{rel_node.cardinality[2]}: maximum cardinality #{rel_node.cardinality[1]} must be positive"
127
+ end
128
+ unless context.classes.include? rel_node.to_class_name.text
129
+ raise ADSLError, "Unknown class name #{rel_node.to_class_name.text} in relation #{klass.name}.#{rel_node.name.text} on line #{rel_node.to_class_name.lineno}"
130
+ end
131
+
132
+ rel.to_class = context.classes[rel_node.to_class_name.text][1]
133
+ rel.cardinality = rel_node.cardinality
134
+ if rel_node.inverse_of_name
135
+ target_class = context.classes[rel.to_class.name][1]
136
+ inverse_of_node, inverse_of = context.relations[target_class.name][rel_node.inverse_of_name.text]
137
+
138
+ while inverse_of_node.nil?
139
+ inverse_of_node, inverse_of = context.relations[target_class.name][rel_node.inverse_of_name.text]
140
+ target_class = target_class.parent
141
+ raise ADSLError, "Unknown relation to which #{rel.from_class.name}.#{rel.name} relation is inverse to: #{rel.to_class.name}.#{rel_node.inverse_of_name.text} on line #{rel_node.inverse_of_name.lineno}" if target_class.nil?
142
+ end
143
+
144
+ if inverse_of_node.inverse_of_name
145
+ raise ADSLError, "Relation #{rel.from_class.name}.#{rel.name} cannot be inverse to an inverse relation #{rel.to_class.name}.#{rel_node.inverse_of_name.text} on line #{rel_node.inverse_of_name.lineno}"
146
+ end
147
+ rel.inverse_of = inverse_of
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ class ADSLRelation < ADSLNode
154
+ node_type :cardinality, :to_class_name, :name, :inverse_of_name
155
+ end
156
+
157
+ class ADSLIdent < ADSLNode
158
+ node_type :text
159
+ end
160
+
161
+ class ADSLTypecheckResolveContext
162
+ attr_accessor :classes, :relations, :actions, :invariants, :var_stack
163
+
164
+ class ADSLStackFrame < ActiveSupport::OrderedHash
165
+ attr_accessor :var_write_listeners
166
+ attr_accessor :var_read_listeners
167
+
168
+ def initialize
169
+ super
170
+ @var_write_listeners = []
171
+ @var_read_listeners = []
172
+ end
173
+
174
+ def on_var_read(&block)
175
+ listeners = @var_read_listeners
176
+ listeners.push block
177
+ end
178
+
179
+ def on_var_write(&block)
180
+ listeners = @var_write_listeners
181
+ listeners.push block
182
+ end
183
+
184
+ def fire_write_event(name)
185
+ listeners = @var_write_listeners
186
+ listeners.each do |listener|
187
+ listener.call name
188
+ end
189
+ end
190
+
191
+ def fire_read_event(name)
192
+ listeners = @var_read_listeners
193
+ listeners.each do |listener|
194
+ listener.call name
195
+ end
196
+ end
197
+
198
+ def dup
199
+ other = ADSLStackFrame.new
200
+ self.each do |key, val|
201
+ other[key] = val.dup
202
+ end
203
+ other.var_write_listeners = @var_write_listeners.dup
204
+ other.var_read_listeners = @var_read_listeners.dup
205
+ other
206
+ end
207
+
208
+ def clone
209
+ dup
210
+ end
211
+ end
212
+
213
+ def initialize
214
+ # name => [astnode, dsobj]
215
+ @classes = ActiveSupport::OrderedHash.new
216
+ # classname => name => [astnode, dsobj]
217
+ @relations = ActiveSupport::OrderedHash.new { |hash, key| hash[key] = ActiveSupport::OrderedHash.new }
218
+ # stack of name => [astnode, dsobj]
219
+ @actions = ActiveSupport::OrderedHash.new
220
+ @invariants = []
221
+ @var_stack = []
222
+ end
223
+
224
+ def initialize_copy(source)
225
+ super
226
+ source.classes.each do |name, value|
227
+ @classes[name] = value.dup
228
+ end
229
+ source.relations.each do |class_name, class_entry|
230
+ entries = @relations[class_name]
231
+ class_entry.each do |name, value|
232
+ entries[name] = value.dup
233
+ end
234
+ end
235
+ @actions = source.actions.dup
236
+ @invariants = source.invariants.dup
237
+ @var_stack = []
238
+ source.var_stack.each do |frame|
239
+ @var_stack << frame.dup
240
+ end
241
+ end
242
+
243
+ def on_var_write(&block)
244
+ @var_stack.last.on_var_write(&block)
245
+ end
246
+
247
+ def on_var_read(&block)
248
+ @var_stack.last.on_var_read(&block)
249
+ end
250
+
251
+ def in_stack_frame
252
+ push_frame
253
+ yield
254
+ ensure
255
+ pop_frame
256
+ end
257
+
258
+ def push_frame
259
+ @var_stack.push ADSLStackFrame.new
260
+ end
261
+
262
+ def pop_frame
263
+ @var_stack.pop
264
+ end
265
+
266
+ def define_var(var, node)
267
+ raise ADSLError, "Defining variables on a stack with no stack frames" if @var_stack.empty?
268
+ prev_var_node, prev_var = lookup_var var.name
269
+ raise ADSLError, "Duplicate identifier '#{var.name}' on line #{node.lineno}; previous definition on line #{prev_var_node.lineno}" unless prev_var.nil?
270
+ @var_stack.last[var.name] = [node, var]
271
+ @var_stack.last.fire_write_event var.name
272
+ return var
273
+ end
274
+
275
+ def redefine_var(var, node)
276
+ @var_stack.length.times do |frame_index|
277
+ frame = @var_stack[frame_index]
278
+ next unless frame.include? var.name
279
+ old_var = frame[var.name][1]
280
+
281
+ raise ADSL::ADSLError, "Unmatched type '#{var.type.name}' for variable '#{var.name}' on line #{node.lineno}" if var.type != old_var.type
282
+
283
+ frame[var.name][1] = var
284
+
285
+ @var_stack[frame_index..-1].reverse.each do |subframe|
286
+ subframe.fire_write_event var.name
287
+ end
288
+
289
+ return var
290
+ end
291
+ return define_var var, node
292
+ end
293
+
294
+ def lookup_var(name, fire_read_event=true)
295
+ @var_stack.length.times do |index|
296
+ frame = @var_stack[index]
297
+ next if frame[name].nil?
298
+ var = frame[name]
299
+
300
+ if fire_read_event
301
+ @var_stack[index..-1].reverse.each do |subframe|
302
+ subframe.fire_read_event name
303
+ end
304
+ end
305
+
306
+ # handle events here, none defined atm
307
+ return var
308
+ end
309
+ nil
310
+ end
311
+
312
+ def self.context_vars_that_differ(*contexts)
313
+ vars_per_context = []
314
+ contexts.each do |context|
315
+ vars_per_context << context.var_stack.inject({}) { |so_far, frame| so_far.merge! frame }
316
+ end
317
+ all_vars = vars_per_context.map{ |c| c.keys }.flatten.uniq
318
+ packed = {}
319
+ all_vars.each do |v|
320
+ packed[v] = vars_per_context.map{ |vpc| vpc[v][1] }
321
+ end
322
+ packed.delete_if { |v, vars| vars.uniq.length == 1 }
323
+ packed
324
+ end
325
+ end
326
+
327
+ class ADSLAction < ADSLNode
328
+ node_type :name, :arg_cardinalities, :arg_names, :arg_types, :block
329
+
330
+ def typecheck_and_resolve(context)
331
+ old_action_node, old_action = context.actions[@name.text]
332
+ raise ADSLError, "Duplicate action name #{@name.text} on line #{@name.lineno}; first definition on line #{old_action_node.name.lineno}" unless old_action.nil?
333
+ arguments = []
334
+ cardinalities = []
335
+ block = nil
336
+ context.in_stack_frame do
337
+ @arg_names.length.times do |i|
338
+ cardinality = @arg_cardinalities[i]
339
+ if cardinality[0] > cardinality[1]
340
+ raise ADSLError, "Invalid cardinality of argument #{@arg_names[i].text} of action #{@name.text} on line #{cardinality[2]}: minimum cardinality #{cardinality[0]} must not be greater than the maximum cardinality #{cardinality[1]}"
341
+ end
342
+ if cardinality[1] == 0
343
+ raise ADSLError, "Invalid cardinality of relation #{@arg_names[i].text} of action #{@name.text} on line #{cardinality[2]}: maximum cardinality #{cardinality[1]} must be positive"
344
+ end
345
+ cardinality = cardinality.first 2
346
+
347
+ klass_node, klass = context.classes[@arg_types[i].text]
348
+ raise ADSLError, "Unknown class #{@arg_types[i].text} on line #{@arg_types[i].lineno}" if klass.nil?
349
+ var = DS::DSVariable.new :name => @arg_names[i].text, :type => klass
350
+ context.define_var var, @arg_types[i]
351
+ arguments << var
352
+ cardinalities << cardinality
353
+ end
354
+ block = @block.typecheck_and_resolve context, false
355
+ end
356
+ action = DS::DSAction.new :name => @name.text, :args => arguments, :cardinalities => cardinalities, :block => block
357
+ context.actions[action.name] = [self, action]
358
+ return action
359
+ end
360
+ end
361
+
362
+ class ADSLBlock < ADSLNode
363
+ node_type :statements
364
+
365
+ def typecheck_and_resolve(context, open_subcontext=true)
366
+ context.push_frame if open_subcontext
367
+ stmts = @statements.map{ |node| node.typecheck_and_resolve context }.flatten
368
+ return DS::DSBlock.new :statements => stmts
369
+ ensure
370
+ context.pop_frame if open_subcontext
371
+ end
372
+ end
373
+
374
+ class ADSLAssignment < ADSLNode
375
+ node_type :var_name, :objset
376
+
377
+ def typecheck_and_resolve(context)
378
+ objset = @objset.typecheck_and_resolve context
379
+ @var = DS::DSVariable.new :name => @var_name.text, :type => objset.type
380
+ context.redefine_var @var, @var_name
381
+ return DS::DSAssignment.new :var => @var, :objset => objset
382
+ end
383
+ end
384
+
385
+ class ADSLCreateObj < ADSLNode
386
+ node_type :var_name, :class_name
387
+
388
+ def typecheck_and_resolve(context)
389
+ klass_node, klass = context.classes[@class_name.text]
390
+ raise ADSLError, "Undefined class #{@class_name.text} referred to at line #{@class_name.lineno}" if klass.nil?
391
+ create_obj = DS::DSCreateObj.new :klass => klass
392
+ if @var_name
393
+ var = DS::DSVariable.new :name => @var_name.text, :type => klass
394
+ objset = DS::DSCreateObjset.new :createobj => create_obj
395
+ assignment = DS::DSAssignment.new :var => var, :objset => objset
396
+ context.redefine_var var, @var_name
397
+ return [create_obj, assignment]
398
+ end
399
+ return create_obj
400
+ end
401
+ end
402
+
403
+ class ADSLForEach < ADSLNode
404
+ node_type :var_name, :objset, :block
405
+
406
+ def typecheck_and_resolve(context)
407
+ before_context = context.dup
408
+ objset = @objset.typecheck_and_resolve context
409
+
410
+ temp_iterator_objset = ADSL::ADSLDummyObjset.new :type => objset.type
411
+ assignment = ADSL::ADSLAssignment.new :lineno => @lineno, :var_name => @var_name, :objset => temp_iterator_objset
412
+ @block.statements = [assignment, @block.statements].flatten
413
+
414
+ vars_written_to = Set[]
415
+ vars_read = Set[]
416
+ vars_read_before_being_written_to = Set[]
417
+ context.on_var_write do |name|
418
+ vars_written_to << name
419
+ end
420
+ context.on_var_read do |name|
421
+ var_node, var = context.lookup_var name, false
422
+ vars_read_before_being_written_to << name unless
423
+ vars_written_to.include?(name) or vars_read_before_being_written_to.include? name
424
+ vars_read << name unless vars_read.include? name
425
+ end
426
+
427
+ context.push_frame
428
+ block = @block.typecheck_and_resolve context
429
+ context.pop_frame
430
+
431
+ vars_read_before_being_written_to.each do |var_name|
432
+ vars_read_before_being_written_to.delete var_name unless vars_written_to.include? var_name
433
+ end
434
+
435
+ flat = true
436
+ # flat = false unless vars_read_before_being_written_to.empty?
437
+
438
+ if flat
439
+ for_each = DS::DSFlatForEach.new :objset => objset, :block => block
440
+ else
441
+ for_each = DS::DSForEach.new :objset => objset, :block => block
442
+ end
443
+
444
+ vars_read_before_being_written_to.each do |var_name|
445
+ before_var_node, before_var = before_context.lookup_var var_name, false
446
+ inside_var_node, inside_var = context.lookup_var var_name, false
447
+ lambda_objset = DS::DSForEachPreLambdaObjset.new :for_each => for_each, :before_var => before_var, :inside_var => inside_var
448
+ var = DS::DSVariable.new :name => var_name, :type => before_var.type
449
+ assignment = DS::DSAssignment.new :var => var, :objset => lambda_objset
450
+ block.replace before_var, var
451
+ block.statements.unshift assignment
452
+ end
453
+
454
+ iterator_objset = DS::DSForEachIteratorObjset.new :for_each => for_each
455
+ block.replace temp_iterator_objset, iterator_objset
456
+ return for_each
457
+ end
458
+
459
+ def list_creations
460
+ @block.list_creations
461
+ end
462
+ end
463
+
464
+ class ADSLEither < ADSLNode
465
+ node_type :blocks
466
+
467
+ def typecheck_and_resolve(context)
468
+ context.push_frame
469
+ contexts = [context]
470
+ (1..@blocks.length-1).each do |i|
471
+ contexts << context.dup
472
+ end
473
+
474
+ blocks = []
475
+ @blocks.length.times do |i|
476
+ blocks << @blocks[i].typecheck_and_resolve(contexts[i])
477
+ end
478
+
479
+ contexts.each do |context|
480
+ context.pop_frame
481
+ end
482
+
483
+ either = DS::DSEither.new :blocks => blocks
484
+
485
+ lambdas = []
486
+ ADSLTypecheckResolveContext::context_vars_that_differ(*contexts).each do |var_name, vars|
487
+ var = DS::DSVariable.new :name => var_name, :type => vars.first.type
488
+ objset = DS::DSEitherLambdaObjset.new :either => either, :vars => vars
489
+ assignment = DS::DSAssignment.new :var => var, :objset => objset
490
+ context.redefine_var var, nil
491
+ lambdas << assignment
492
+ end
493
+
494
+ return [ either, lambdas ]
495
+ end
496
+
497
+ def list_entity_classes_written_to
498
+ @blocks.map{ block.list_entity_classes_written_to }.flatten
499
+ end
500
+ end
501
+
502
+ class ADSLDeleteObj < ADSLNode
503
+ node_type :objset
504
+
505
+ def typecheck_and_resolve(context)
506
+ objset = @objset.typecheck_and_resolve context
507
+ return DS::DSDeleteObj.new :objset => objset
508
+ end
509
+ end
510
+
511
+ def self.find_relation(context, from_type, rel_name, lineno, to_type=nil)
512
+ iter = from_type
513
+ relation_node, relation = context.relations[iter.name][rel_name]
514
+ while relation.nil?
515
+ iter = iter.parent
516
+ raise ADSLError, "Unknown relation #{from_type.name}.#{rel_name} on line #{lineno}" if iter.nil?
517
+ relation_node, relation = context.relations[iter.name][rel_name]
518
+ end
519
+
520
+ unless to_type.nil?
521
+ raise ADSLError, "Mismatched right-hand-side type for relation #{from_type.name}.#{rel_name} on line #{lineno}. Expected #{relation.to_class.name} but was #{to_type.name}" unless relation.to_class.superclass_of? to_type
522
+ end
523
+
524
+ relation
525
+ end
526
+
527
+ class ADSLCreateTup < ADSLNode
528
+ node_type :objset1, :rel_name, :objset2
529
+
530
+ def typecheck_and_resolve(context)
531
+ objset1 = @objset1.typecheck_and_resolve context
532
+ objset2 = @objset2.typecheck_and_resolve context
533
+ relation = ADSL::find_relation context, objset1.type, @rel_name.text, @rel_name.lineno, objset2.type
534
+ return DS::DSCreateTup.new :objset1 => objset1, :relation => relation, :objset2 => objset2
535
+ end
536
+ end
537
+
538
+ class ADSLDeleteTup < ADSLNode
539
+ node_type :objset1, :rel_name, :objset2
540
+
541
+ def typecheck_and_resolve(context)
542
+ objset1 = @objset1.typecheck_and_resolve context
543
+ objset2 = @objset2.typecheck_and_resolve context
544
+ relation = ADSL::find_relation context, objset1.type, @rel_name.text, @rel_name.lineno, objset2.type
545
+ return DS::DSDeleteTup.new :objset1 => objset1, :relation => relation, :objset2 => objset2
546
+ end
547
+ end
548
+
549
+ class ADSLAllOf < ADSLNode
550
+ node_type :class_name
551
+
552
+ def typecheck_and_resolve(context)
553
+ klass_node, klass = context.classes[@class_name.text]
554
+ raise ADSLError, "Unknown class name #{@class_name.text} on line #{@class_name.lineno}" if klass.nil?
555
+ return DS::DSAllOf.new :klass => klass
556
+ end
557
+ end
558
+
559
+ class ADSLSubset < ADSLNode
560
+ node_type :objset
561
+
562
+ def typecheck_and_resolve(context)
563
+ objset = @objset.typecheck_and_resolve context
564
+ return DS::DSSubset.new :objset => objset
565
+ end
566
+ end
567
+
568
+ class ADSLOneOf < ADSLNode
569
+ node_type :objset
570
+
571
+ def typecheck_and_resolve(context)
572
+ objset = @objset.typecheck_and_resolve context
573
+ return DS::DSOneOf.new :objset => objset
574
+ end
575
+ end
576
+
577
+ class ADSLVariable < ADSLNode
578
+ node_type :var_name
579
+
580
+ def typecheck_and_resolve(context)
581
+ var_node, var = context.lookup_var @var_name.text
582
+ raise ADSLError, "Undefined variable #{@var_name.text} on line #{@var_name.lineno}" if var.nil?
583
+ return var
584
+ end
585
+ end
586
+
587
+ class ADSLDereference < ADSLNode
588
+ node_type :objset, :rel_name
589
+
590
+ def typecheck_and_resolve(context)
591
+ objset = @objset.typecheck_and_resolve context
592
+ klass = objset.type
593
+ relation = ADSL::find_relation context, objset.type, @rel_name.text, @rel_name.lineno
594
+ return DS::DSDereference.new :objset => objset, :relation => relation
595
+ end
596
+ end
597
+
598
+ class ADSLInvariant < ADSLNode
599
+ node_type :name, :formula
600
+
601
+ def typecheck_and_resolve(context)
602
+ formula = @formula.typecheck_and_resolve context
603
+ name = @name.nil? ? nil : @name.text
604
+ return DS::DSInvariant.new :name => name, :formula => formula
605
+ end
606
+ end
607
+
608
+ class ADSLBoolean < ADSLNode
609
+ node_type :bool_value
610
+
611
+ def typecheck_and_resolve(context)
612
+ return DS::DSBoolean::TRUE if @bool_value
613
+ return DS::DSBoolean::FALSE
614
+ end
615
+ end
616
+
617
+ class ADSLForAll < ADSLNode
618
+ node_type :vars, :subformula
619
+
620
+ def typecheck_and_resolve(context)
621
+ context.in_stack_frame do
622
+ vars = []
623
+ objsets = []
624
+ @vars.each do |var_node, objset_node|
625
+ objset = objset_node.typecheck_and_resolve context
626
+
627
+ var = DS::DSVariable.new :name => var_node.text, :type => objset.type
628
+ context.define_var var, var_node
629
+
630
+ vars << var
631
+ objsets << objset
632
+ end
633
+ subformula = @subformula.typecheck_and_resolve context
634
+ return DS::DSForAll.new :vars => vars, :objsets => objsets, :subformula => subformula
635
+ end
636
+ end
637
+ end
638
+
639
+ class ADSLExists < ADSLNode
640
+ node_type :vars, :subformula
641
+
642
+ def typecheck_and_resolve(context)
643
+ context.in_stack_frame do
644
+ vars = []
645
+ objsets = []
646
+ @vars.each do |var_node, objset_node|
647
+ objset = objset_node.typecheck_and_resolve context
648
+
649
+ var = DS::DSVariable.new :name => var_node.text, :type => objset.type
650
+ context.define_var var, var_node
651
+
652
+ vars << var
653
+ objsets << objset
654
+ end
655
+ subformula = @subformula.nil? ? nil : @subformula.typecheck_and_resolve(context)
656
+ return DS::DSExists.new :vars => vars, :objsets => objsets, :subformula => subformula
657
+ end
658
+ end
659
+ end
660
+
661
+ class ADSLNot < ADSLNode
662
+ node_type :subformula
663
+
664
+ def typecheck_and_resolve(context)
665
+ subformula = @subformula.typecheck_and_resolve context
666
+ raise "Substatement not a formula on line #{@subformula.lineno}" unless subformula.type == :formula
667
+ return subformula.subformula if subformula.is_a? DS::DSNot
668
+ return DS::DSNot.new :subformula => subformula
669
+ end
670
+ end
671
+
672
+ class ADSLAnd < ADSLNode
673
+ node_type :subformulae
674
+
675
+ def typecheck_and_resolve(context)
676
+ subformulae = @subformulae.map{ |o| o.typecheck_and_resolve context }
677
+ subformulae.each do |subformula|
678
+ raise "Substatement not a formula on line #{subformula.lineno}" unless subformula.type == :formula
679
+ end
680
+ flattened_subformulae = []
681
+ subformulae.each do |subformula|
682
+ if subformula.is_a? DS::DSAnd
683
+ flattened_subformulae += subformula.subformulae
684
+ else
685
+ flattened_subformulae << subformula
686
+ end
687
+ end
688
+ return DS::DSAnd.new :subformulae => flattened_subformulae
689
+ end
690
+ end
691
+
692
+ class ADSLOr < ADSLNode
693
+ node_type :subformulae
694
+
695
+ def typecheck_and_resolve(context)
696
+ subformulae = @subformulae.map{ |o| o.typecheck_and_resolve context }
697
+ subformulae.each do |subformula|
698
+ raise "Substatement not a formula on line #{subformula.lineno}" unless subformula.type == :formula
699
+ end
700
+ flattened_subformulae = []
701
+ subformulae.each do |subformula|
702
+ if subformula.is_a? DS::DSOr
703
+ flattened_subformulae += subformula.subformulae
704
+ else
705
+ flattened_subformulae << subformula
706
+ end
707
+ end
708
+ return DS::DSOr.new :subformulae => flattened_subformulae
709
+ end
710
+ end
711
+
712
+ class ADSLEquiv < ADSLNode
713
+ node_type :subformulae
714
+
715
+ def typecheck_and_resolve(context)
716
+ subformulae = @subformulae.map{ |o| o.typecheck_and_resolve context }
717
+ subformulae.each do |subformula|
718
+ raise "Substatement not a formula on line #{subformula.lineno}" unless subformula.type == :formula
719
+ end
720
+ return DS::DSEquiv.new :subformulae => subformulae
721
+ end
722
+ end
723
+
724
+ class ADSLImplies < ADSLNode
725
+ node_type :subformula1, :subformula2
726
+
727
+ def typecheck_and_resolve(context)
728
+ subformula1 = @subformula1.typecheck_and_resolve context
729
+ subformula2 = @subformula2.typecheck_and_resolve context
730
+
731
+ [subformula1, subformula2].each do |subformula|
732
+ raise "Substatement not a formula on line #{subformula.lineno}" unless subformula.type == :formula
733
+ end
734
+ return DS::DSImplies.new :subformula1 => subformula1, :subformula2 => subformula2
735
+ end
736
+ end
737
+
738
+ class ADSLEqual < ADSLNode
739
+ node_type :objsets
740
+
741
+ def typecheck_and_resolve(context)
742
+ objsets = @objsets.map{ |o| o.typecheck_and_resolve context }
743
+
744
+ types = objsets.map{ |o| o.type }.uniq
745
+ while types.length > 1
746
+ type1 = types.pop
747
+ type2 = types.pop
748
+ if type1.superclass_of? type2
749
+ types << type2
750
+ elsif type2.superclass_of? type1
751
+ types << type1
752
+ else
753
+ raise ADSLError, "Object sets are not of compatible types: #{objsets.map { |o| o.type.name }.join(", ")}"
754
+ end
755
+ end
756
+
757
+ return DS::DSEqual.new :objsets => objsets
758
+ end
759
+ end
760
+
761
+ class ADSLIn < ADSLNode
762
+ node_type :objset1, :objset2
763
+
764
+ def typecheck_and_resolve(context)
765
+ objset1 = @objset1.typecheck_and_resolve context
766
+ objset2 = @objset2.typecheck_and_resolve context
767
+ raise ADSLError, "Object sets are not of compatible types: #{objset1.type.name}, #{objset2.type.name}" unless objset2.type.superclass_of? objset1.type
768
+ return DS::DSIn.new :objset1 => objset1, :objset2 => objset2
769
+ end
770
+ end
771
+
772
+ class ADSLEmpty < ADSLNode
773
+ node_type :objset
774
+
775
+ def typecheck_and_resolve(context)
776
+ objset = @objset.typecheck_and_resolve context
777
+ return DS::DSEmpty.new :objset => objset
778
+ end
779
+ end
780
+ end