falluto 0.0.1 → 0.0.9

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,410 @@
1
+ require 'set'
2
+
3
+ require 'rubygems' # to load treetop
4
+ require 'treetop' # to build the parser
5
+
6
+ require 'falluto/grammar'
7
+ require 'falluto/nusmv'
8
+ require 'falluto/ruby_extensions'
9
+ require 'falluto/symboltable'
10
+
11
+ class UndeclaredFault < Exception ; end
12
+ class RedefinedFault < Exception ; end
13
+
14
+ class CompilerContext
15
+ attr_reader :current_module, :modules
16
+ attr_accessor :variable
17
+
18
+ def initialize
19
+ @modules = SymbolTable.new
20
+ end
21
+
22
+ def <<(m)
23
+ @modules << m
24
+ @current_module = m
25
+ end
26
+
27
+ def [](type)
28
+ @modules[type]
29
+ end
30
+
31
+ def main_module
32
+ @modules['main']
33
+ end
34
+
35
+ end
36
+
37
+
38
+ class Compiler
39
+
40
+ attr_reader :input_string, :compiled_string, :parsed_tree, :specs
41
+
42
+ def initialize
43
+ @parser = Falluto::GrammarParser.new
44
+ #@parser.root = "Choose your favorite rule"
45
+ @generator = Falluto::NuSMV::CodeGenerator.new
46
+ @context = CompilerContext.new
47
+ @specs = []
48
+ end
49
+
50
+ def run input
51
+ @input_string = input
52
+ @parsed_tree = @parser.parse @input_string
53
+ if @parsed_tree
54
+ @compiled_string = @parsed_tree.send :compile, self
55
+ end
56
+ end
57
+
58
+ def auxiliar_variables
59
+ variables = Set.new
60
+
61
+ each_module do |m|
62
+ m.each_fault do |f|
63
+ f.each_auxiliar_variable do |v|
64
+ variables << v.name
65
+ end
66
+ variables << f.precondition_variable
67
+ variables << f.restore_variable
68
+ variables << f.fairness_variable
69
+ end
70
+ end
71
+
72
+ variables.sort
73
+ end
74
+
75
+ def each_module &block
76
+ @context.modules.each{|m| yield m}
77
+ end
78
+
79
+ def compile node
80
+ method = nil
81
+ begin
82
+ method = "compile_#{node.class.human_name}"
83
+ #puts "compiling with #{method} '#{node.text}'"
84
+ send method, node
85
+ rescue => e
86
+ puts e.message
87
+ puts e.backtrace
88
+ raise "[ERROR] Don't know how to compile #{node.class} (#{node.text})"
89
+ end
90
+ end
91
+
92
+ # Recursively compile a syntax node descending into its elements.
93
+ def compile_treetop_runtime_syntax_node node
94
+ if node.elements
95
+ node.elements.map{|element|
96
+ # puts "Invoking on #{element.class}"
97
+ compile element}.to_s
98
+ else
99
+ node.text
100
+ end
101
+ end
102
+
103
+ def compile_falluto_module_declaration_node node
104
+ m = Falluto::NuSMV::Module.new node.name
105
+ @context << m
106
+
107
+ result = "MODULE " + node.signature + "\n"
108
+ result << compile(node.declarations)
109
+ if @context.current_module.has_faults?
110
+ result << dump_faulty_module_declarations
111
+ result << dump_system_effect_declaration
112
+ end
113
+ result
114
+ end
115
+
116
+ # Create a new fault object for this declaration.
117
+ # No code is output for this construct.
118
+ #
119
+ def compile_falluto_fault_declaration_node node
120
+ modname = @context.current_module.name
121
+ name = node.name
122
+ precondition = node.precondition
123
+ restores = node.restores
124
+ effect = node.effect
125
+
126
+ f = Falluto::NuSMV::Fault.new modname, name, precondition, restores, effect
127
+ raise RedefinedFault.new(f.name) if @context.current_module.is_defined? f
128
+ @context.current_module.add_fault f
129
+ ''
130
+ end
131
+
132
+ # Compile the following construct
133
+ #
134
+ # next(v) := val disabled_by {f,g};
135
+ #
136
+ # into
137
+ #
138
+ # next(v) :=
139
+ # case
140
+ # (1) & !f.active & !g.active : compile(val);
141
+ # 1 : v;
142
+ # esac;
143
+ #
144
+ def compile_falluto_fault_assignment_node node
145
+ variable = node.variable
146
+ value = node.value
147
+ faults = @context.current_module.get_faults *node.faults
148
+ raise UndeclaredFault.new fname if faults.any?{ |f| f.nil? }
149
+ @context.variable = variable
150
+
151
+ new_condition = build_condition_with_faults "1", faults
152
+
153
+ <<-END
154
+ next(#{@context.variable}) :=
155
+ case
156
+ #{new_condition} : #{compile value};
157
+ 1 : #{@context.variable};
158
+ esac;
159
+ END
160
+
161
+ end
162
+
163
+ # Compile the following construct
164
+ #
165
+ # next(v) := val;
166
+ #
167
+ # into
168
+ #
169
+ # next(v) := compile(val);
170
+ #
171
+ # so that we can propagate fault compilation into the value.
172
+ # (when val is a 'case' construct)
173
+ #
174
+ def compile_falluto_assignment_node node
175
+ variable = node.variable
176
+ value = node.value
177
+ @context.variable = variable
178
+ %Q| next(#{@context.variable}) := #{compile value};\n|
179
+ end
180
+
181
+ # Compile the following construct
182
+ #
183
+ # next(v) :=
184
+ # case
185
+ # c_1 : v_1 disabled_by {f, g};
186
+ # ...
187
+ # c_n : v_n;
188
+ # esac;
189
+ #
190
+ # into
191
+ #
192
+ # next(v) :=
193
+ # case
194
+ # (c_1) & !f.active & !g.active : compile(v1);
195
+ # ...
196
+ # c_n : v_n;
197
+ # 1 : v;
198
+ # esac;
199
+ #
200
+ def compile_falluto_case_node node
201
+ new_cases = []
202
+ has_faults = false
203
+ node.each_case do |c|
204
+ has_faults ||= c.has_faults?
205
+ new_cases << c.compile(self)
206
+ end
207
+ new_cases << " 1 : #{@context.variable};\n" if has_faults
208
+
209
+ "case\n" + new_cases.join('') + "esac"
210
+ end
211
+
212
+ # Compile the folllowing construct
213
+ #
214
+ # c : v disabled_by {f,g};
215
+ #
216
+ # into
217
+ #
218
+ # (c) & !f.active & !g.active : v;
219
+ #
220
+ # if the construct is not disabled by faults,
221
+ # then the input code is unchanged.
222
+ #
223
+ def compile_falluto_case_element_node node
224
+ condition = node.condition
225
+ value = node.value
226
+
227
+ if node.has_faults?
228
+ faults = @context.current_module.get_faults *node.faults
229
+ raise UndeclaredFault.new fname if faults.any?{ |f| f.nil? }
230
+ new_condition = build_condition_with_faults condition, faults
231
+ add_condition_to_faults condition, faults
232
+ "#{new_condition} : #{value};\n"
233
+ else
234
+ "#{condition} : #{value};\n"
235
+ end
236
+ end
237
+
238
+ # Compile a variable will output the original code.
239
+ # However, we also need to track variables and their types
240
+ # to modify the model specifications.
241
+ def compile_falluto_var_decl_node node
242
+ v = Falluto::NuSMV::Variable.new node.name, node.vartype
243
+ @context.current_module.add_variable v
244
+ compile_treetop_runtime_syntax_node node
245
+ end
246
+
247
+ # Compile the following construct
248
+ #
249
+ # LTLSPEC s
250
+ #
251
+ # into
252
+ #
253
+ # LTLSPEC ((faults and processes are fair) -> (s))
254
+ #
255
+ def compile_falluto_ltl_spec_node node
256
+ spec = node.specification
257
+ sf_list = ['1']
258
+
259
+ @context.main_module.each_variable do |variable|
260
+ type = @context[variable.type]
261
+ # NOTE nusmvmodule will be nil if type is undeclared
262
+ sf = dump_module_strong_fairness(type, variable.name) unless type.nil?
263
+ sf_list << sf if sf
264
+ end
265
+
266
+ @specs << spec
267
+
268
+ "LTLSPEC (( #{sf_list.join(" & ")} ) -> #{spec})"
269
+ end
270
+
271
+ def no_fault_active faults
272
+ faults.collect{|f| "! #{f.name}.#{f.active_variable}"}.join(' & ')
273
+ end
274
+
275
+ def build_condition_with_faults condition, faults
276
+ "(#{condition}) & #{no_fault_active faults}"
277
+ end
278
+
279
+ def add_condition_to_faults condition, faults
280
+ faults.each { |fault| fault.add_auxiliar_variable condition }
281
+ end
282
+
283
+ def dump_faulty_module_declarations
284
+ decls = ''
285
+
286
+ @context.current_module.each_fault do |fault|
287
+ decls << "-- Begin declarations for (#{fault.name})\n"
288
+ decls << declare_fault_precondition(fault)
289
+ decls << declare_fault_restore(fault)
290
+ decls << declare_fault_instance(fault)
291
+ decls << declare_auxiliar_variables(fault)
292
+ decls << declare_fault_fairness(fault)
293
+ decls << "-- End declarations for (#{fault.name})\n"
294
+ end
295
+
296
+ decls << "FAIRNESS running\n" if @context.current_module.has_faults?
297
+ decls
298
+ end
299
+
300
+ def dump_system_effect_declaration
301
+ decls = ''
302
+
303
+ @context.current_module.each_fault do |fault|
304
+ name = @context.current_module.name
305
+ decls << "-- Begin system effect for (#{name}, #{fault.name})\n"
306
+ decls << declare_sytem_effect(fault)
307
+ decls << "-- End system effect for (#{name}, #{fault.name})\n"
308
+ end
309
+
310
+ decls
311
+ end
312
+
313
+ # NOTE: Can we return true when the module has no faults?
314
+ def dump_module_strong_fairness m, inst
315
+ sf = []
316
+ m.each_fault{ |f| sf << f.strong_fairness(inst) }
317
+
318
+ if sf.empty?
319
+ nil
320
+ else
321
+ "(#{sf.join(" & ")})"
322
+ end
323
+ end
324
+
325
+ def declare_fault_precondition fault
326
+ %Q|DEFINE #{fault.precondition_variable} := (#{fault.precondition});\n|
327
+ end
328
+
329
+ def declare_fault_restore fault
330
+ %Q|DEFINE #{fault.restore_variable} := (#{fault.restore});\n|
331
+ end
332
+
333
+ def declare_fault_instance fault
334
+ %Q|VAR #{fault.name} : process #{fault.instance_signature};\n|
335
+ end
336
+
337
+ def declare_auxiliar_variables fault
338
+ str = ''
339
+ fault.each_auxiliar_variable do |v|
340
+ str << %Q|DEFINE #{v.name} := (#{v.condition});\n|
341
+ end
342
+ str
343
+ end
344
+
345
+ def declare_fault_fairness fault
346
+ fv = fault.fairness_variable
347
+ result =<<-END_DECL
348
+ VAR #{fv} : boolean;
349
+ ASSIGN
350
+ next(#{fv}) :=
351
+ case
352
+ #{fault.fairness_condition} & !#{fv} : 1;
353
+ 1 : 0;
354
+ esac;
355
+ END_DECL
356
+ end
357
+
358
+ def declare_sytem_effect fault
359
+ # The system effect is a module which takes all variables affected by
360
+ # faults as parameters and modifies them when the fault occurs.
361
+
362
+ declare_fault_module(fault) +
363
+ declare_affected_variables_modifications(fault) +
364
+ "FAIRNESS running\n"
365
+ end
366
+
367
+ def declare_fault_module fault
368
+ instance_signature = fault.instance_signature
369
+ active = fault.active_variable
370
+ pre = fault.precondition_variable
371
+ restore = fault.restore_variable
372
+
373
+ <<-END_DECL
374
+ MODULE #{instance_signature}
375
+ VAR #{active} : boolean;
376
+ ASSIGN
377
+ next(#{active}) :=
378
+ case
379
+ #{pre} & !#{active} : {0, 1};
380
+ #{active} & next(#{restore}) : 0;
381
+ 1 : #{active};
382
+ esac;
383
+ END_DECL
384
+ end
385
+
386
+ def declare_affected_variables_modifications fault
387
+ decls = ''
388
+
389
+ fault.each_affected_variable_with_effect do |v, effect|
390
+ decls << declare_affected_variable_modification(fault, v, effect)
391
+ end
392
+
393
+ decls
394
+ end
395
+
396
+ def declare_affected_variable_modification fault, variable, effect
397
+ active = fault.active_variable
398
+
399
+ <<-END_DECL
400
+ next(#{variable}) :=
401
+ case
402
+ !#{active} & next(#{active}) : #{effect};
403
+ 1 : #{variable};
404
+ esac;
405
+ END_DECL
406
+ end
407
+
408
+
409
+ end
410
+
@@ -0,0 +1,141 @@
1
+ class Treetop::Runtime::SyntaxNode
2
+ alias text text_value
3
+
4
+ def stripped
5
+ text_value.strip
6
+ end
7
+
8
+ def compile generator
9
+ generator.compile self
10
+ end
11
+ end
12
+
13
+ module Falluto
14
+ class ModuleDeclarationNode < Treetop::Runtime::SyntaxNode
15
+ def name
16
+ module_sign.name.stripped
17
+ end
18
+
19
+ def signature
20
+ module_sign.stripped
21
+ end
22
+
23
+ def declarations
24
+ decls
25
+ end
26
+
27
+ end
28
+
29
+ class FaultDeclarationNode < Treetop::Runtime::SyntaxNode
30
+ def name
31
+ id.stripped
32
+ end
33
+
34
+ def precondition
35
+ fault_pre.simple_expression.stripped
36
+ end
37
+
38
+ def restores
39
+ fault_restores.next_expression.stripped
40
+ end
41
+
42
+ def effect
43
+ result = Hash.new
44
+
45
+ each_effect do |effect_expression|
46
+ var = effect_expression.var_id.stripped
47
+ effect = effect_expression.simple_expression.stripped
48
+ result[var] = effect
49
+ end
50
+
51
+ result
52
+ end
53
+
54
+ def each_effect
55
+ first = fault_effect.list.first
56
+ yield first unless first.text.empty?
57
+ fault_effect.list.rest.elements.each {|e| yield e.fault_effect_expression}
58
+ end
59
+ end
60
+
61
+ class FaultAssignmentNode < Treetop::Runtime::SyntaxNode
62
+ def variable
63
+ var_id.stripped
64
+ end
65
+
66
+ def value
67
+ basic_expr
68
+ end
69
+
70
+ def faults
71
+ [list.first.stripped] + list.rest.elements.map{|e| e.fault.stripped}
72
+ end
73
+ end
74
+
75
+ class AssignmentNode < Treetop::Runtime::SyntaxNode
76
+ def variable
77
+ var_id.stripped
78
+ end
79
+
80
+ def value
81
+ basic_expr
82
+ end
83
+ end
84
+
85
+ class CaseNode < Treetop::Runtime::SyntaxNode
86
+ def each_case &block
87
+ cases.elements.each &block
88
+ end
89
+ end
90
+
91
+ class CaseElementNode < Treetop::Runtime::SyntaxNode
92
+ def condition
93
+ left.stripped
94
+ end
95
+
96
+ def value
97
+ right.stripped
98
+ end
99
+
100
+ def faults
101
+ result = []
102
+
103
+ if has_faults?
104
+ list = disabled_by.list
105
+ result << list.first.stripped
106
+ list.rest.elements.inject(result) do |acc, node|
107
+ acc << node.fault.stripped
108
+ end
109
+ end
110
+
111
+ result
112
+ end
113
+
114
+ def has_faults?
115
+ not disabled_by.elements.nil?
116
+ end
117
+
118
+ end
119
+
120
+ class LtlSpecNode < Treetop::Runtime::SyntaxNode
121
+ def specification
122
+ spec.stripped
123
+ end
124
+ end
125
+
126
+ class VarDeclNode < Treetop::Runtime::SyntaxNode
127
+ def name
128
+ decl_var_id.stripped
129
+ end
130
+
131
+ def vartype
132
+ begin
133
+ t = type.module_type.name.stripped
134
+ rescue NoMethodError
135
+ t = type.stripped
136
+ end
137
+ end
138
+
139
+ end
140
+ end
141
+