pg-verify 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +98 -0
- data/README.md +29 -0
- data/Rakefile +62 -0
- data/bin/console +15 -0
- data/bin/pg-verify.rb +18 -0
- data/bin/setup +8 -0
- data/calc.ebnf +21 -0
- data/data/config/pg-verify.yml +66 -0
- data/data/nusmv.sample.smv +179 -0
- data/data/project-template/.gitignore.resource +4 -0
- data/data/project-template/.pg-verify.yml +0 -0
- data/data/project-template/README.md +18 -0
- data/data/project-template/addon/.keep +0 -0
- data/data/project-template/program-graph.rb.resource +103 -0
- data/devpg +5 -0
- data/doc/examples/railroad_crossing.rb +61 -0
- data/doc/examples/train-tree.rb +43 -0
- data/doc/examples/weidezaun.rb +99 -0
- data/doc/examples/weidezaun.txt +29 -0
- data/doc/expose/definition.png +0 -0
- data/doc/expose/diagram.png +0 -0
- data/doc/expose/expose.md +359 -0
- data/doc/expose/validity.png +0 -0
- data/exe/pg-verify +4 -0
- data/integration_tests/ruby_dsl/001_states.rb +10 -0
- data/integration_tests/ruby_dsl/002_transitions.rb +10 -0
- data/integration_tests/ruby_dsl/003_actions.rb +14 -0
- data/integration_tests/ruby_dsl/004_guards.rb +18 -0
- data/integration_tests/ruby_dsl/005_variables.rb +16 -0
- data/integration_tests/ruby_dsl/006_state_variables.rb +26 -0
- data/integration_tests/ruby_dsl/007_variable_initialization.rb +28 -0
- data/integration_tests/ruby_dsl/008_state_initialization.rb +19 -0
- data/integration_tests/ruby_dsl/009_shared_variables.rb +26 -0
- data/integration_tests/ruby_dsl/010_complex_guards.rb +18 -0
- data/integration_tests/ruby_dsl/011_complex_actions.rb +16 -0
- data/integration_tests/ruby_dsl/012_error_components.rb +9 -0
- data/integration_tests/ruby_dsl/013_hazards.rb +25 -0
- data/integration_tests/ruby_dsl/014_tau_transitions.rb +26 -0
- data/integration_tests/ruby_dsl/015_basic_dcca.rb +19 -0
- data/integration_tests/ruby_dsl/016_pressure_tank.rb +146 -0
- data/lib/pg-verify/cli/cli.rb +235 -0
- data/lib/pg-verify/core/cmd_runner.rb +151 -0
- data/lib/pg-verify/core/core.rb +38 -0
- data/lib/pg-verify/core/extensions/array_extensions.rb +11 -0
- data/lib/pg-verify/core/extensions/enumerable_extensions.rb +19 -0
- data/lib/pg-verify/core/extensions/nil_extensions.rb +7 -0
- data/lib/pg-verify/core/extensions/string_extensions.rb +84 -0
- data/lib/pg-verify/core/shell/colorizer.rb +136 -0
- data/lib/pg-verify/core/shell/shell.rb +0 -0
- data/lib/pg-verify/core/util.rb +146 -0
- data/lib/pg-verify/doctor/doctor.rb +180 -0
- data/lib/pg-verify/ebnf_parser/ast.rb +31 -0
- data/lib/pg-verify/ebnf_parser/ebnf_parser.rb +26 -0
- data/lib/pg-verify/ebnf_parser/expression_parser.rb +177 -0
- data/lib/pg-verify/ebnf_parser/expression_parser2.rb +422 -0
- data/lib/pg-verify/ebnf_parser/expressions.ebnf +33 -0
- data/lib/pg-verify/ebnf_parser/expressions.peg +52 -0
- data/lib/pg-verify/ebnf_parser/parser_result.rb +26 -0
- data/lib/pg-verify/interpret/component_context.rb +125 -0
- data/lib/pg-verify/interpret/graph_context.rb +85 -0
- data/lib/pg-verify/interpret/interpret.rb +142 -0
- data/lib/pg-verify/interpret/pg_script.rb +72 -0
- data/lib/pg-verify/interpret/spec/ltl_builder.rb +90 -0
- data/lib/pg-verify/interpret/spec/spec_context.rb +32 -0
- data/lib/pg-verify/interpret/spec/spec_set_context.rb +67 -0
- data/lib/pg-verify/interpret/transition_context.rb +55 -0
- data/lib/pg-verify/model/allocation_set.rb +28 -0
- data/lib/pg-verify/model/assignment.rb +34 -0
- data/lib/pg-verify/model/component.rb +40 -0
- data/lib/pg-verify/model/dcca/hazard.rb +16 -0
- data/lib/pg-verify/model/dcca.rb +67 -0
- data/lib/pg-verify/model/expression.rb +106 -0
- data/lib/pg-verify/model/graph.rb +58 -0
- data/lib/pg-verify/model/model.rb +10 -0
- data/lib/pg-verify/model/parsed_expression.rb +77 -0
- data/lib/pg-verify/model/simulation/trace.rb +43 -0
- data/lib/pg-verify/model/simulation/variable_state.rb +23 -0
- data/lib/pg-verify/model/source_location.rb +45 -0
- data/lib/pg-verify/model/specs/spec.rb +44 -0
- data/lib/pg-verify/model/specs/spec_result.rb +25 -0
- data/lib/pg-verify/model/specs/spec_set.rb +43 -0
- data/lib/pg-verify/model/specs/specification.rb +50 -0
- data/lib/pg-verify/model/transition.rb +41 -0
- data/lib/pg-verify/model/validation/assignment_to_state_variable_validation.rb +26 -0
- data/lib/pg-verify/model/validation/empty_state_set_validation.rb +18 -0
- data/lib/pg-verify/model/validation/errors.rb +119 -0
- data/lib/pg-verify/model/validation/foreign_assignment_validation.rb +30 -0
- data/lib/pg-verify/model/validation/unknown_token_validation.rb +35 -0
- data/lib/pg-verify/model/validation/validation.rb +23 -0
- data/lib/pg-verify/model/variable.rb +47 -0
- data/lib/pg-verify/model/variable_set.rb +84 -0
- data/lib/pg-verify/nusmv/nusmv.rb +23 -0
- data/lib/pg-verify/nusmv/runner.rb +124 -0
- data/lib/pg-verify/puml/puml.rb +23 -0
- data/lib/pg-verify/shell/loading/line_animation.rb +36 -0
- data/lib/pg-verify/shell/loading/loading_animation.rb +80 -0
- data/lib/pg-verify/shell/loading/loading_prompt.rb +43 -0
- data/lib/pg-verify/shell/loading/no_animation.rb +20 -0
- data/lib/pg-verify/shell/shell.rb +30 -0
- data/lib/pg-verify/simulation/simulation.rb +7 -0
- data/lib/pg-verify/simulation/simulator.rb +90 -0
- data/lib/pg-verify/simulation/state.rb +53 -0
- data/lib/pg-verify/transform/hash_transformation.rb +104 -0
- data/lib/pg-verify/transform/nusmv_transformation.rb +261 -0
- data/lib/pg-verify/transform/puml_transformation.rb +89 -0
- data/lib/pg-verify/transform/transform.rb +8 -0
- data/lib/pg-verify/version.rb +5 -0
- data/lib/pg-verify.rb +47 -0
- data/pg-verify.gemspec +38 -0
- data/sig/pg-verify.rbs +4 -0
- metadata +226 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
|
2
|
+
module PgVerify
|
3
|
+
module Interpret
|
4
|
+
|
5
|
+
class GraphContext
|
6
|
+
|
7
|
+
attr_accessor :parent_script
|
8
|
+
# The list of currently declared components
|
9
|
+
attr_accessor :components
|
10
|
+
attr_accessor :specs
|
11
|
+
attr_accessor :hazards
|
12
|
+
# The name of this graph
|
13
|
+
attr_accessor :name
|
14
|
+
|
15
|
+
def initialize(name, parent_script)
|
16
|
+
@parent_script = parent_script
|
17
|
+
@name = name
|
18
|
+
@components, @specs, @hazards = [], [], []
|
19
|
+
end
|
20
|
+
|
21
|
+
# DSL method for declaring a new component in this graph
|
22
|
+
def graph(name, &blk)
|
23
|
+
raise InvalidDSL_graph.new("Name '#{name}' is neither a symbol nor string") unless name.is_a?(Symbol) || name.is_a?(String)
|
24
|
+
cmp = ComponentContext.new(name, self)
|
25
|
+
cmp.instance_eval(&blk)
|
26
|
+
@components << cmp
|
27
|
+
return cmp
|
28
|
+
end
|
29
|
+
|
30
|
+
def transient(name)
|
31
|
+
cmp = graph(name) do
|
32
|
+
all_states = [ :No, :Yes ]
|
33
|
+
states(*all_states)
|
34
|
+
all_states.product(all_states).each { |s1, s2| transition({ s1 => s2}) }
|
35
|
+
end
|
36
|
+
cmp.represents_fault = true
|
37
|
+
return cmp
|
38
|
+
end
|
39
|
+
|
40
|
+
def persistent(name)
|
41
|
+
cmp = graph(name) do
|
42
|
+
states(:No, :Yes)
|
43
|
+
transition :No => :No
|
44
|
+
transition :No => :Yes
|
45
|
+
end
|
46
|
+
cmp.represents_fault = true
|
47
|
+
return cmp
|
48
|
+
end
|
49
|
+
|
50
|
+
def disable_error(name)
|
51
|
+
cmp = @components.find { |c| c.name == name.to_sym }
|
52
|
+
raise "No such component: #{name}" if cmp.nil?
|
53
|
+
raise "Not an error component: #{name}" unless cmp.represents_fault
|
54
|
+
cmp.transition_list = [ ]
|
55
|
+
cmp.init_expressions = [ "#{name} == No" ]
|
56
|
+
end
|
57
|
+
|
58
|
+
def error(name)
|
59
|
+
return name
|
60
|
+
end
|
61
|
+
|
62
|
+
# DSL method for declaring a new specification in this graph
|
63
|
+
def specify(text, &blk)
|
64
|
+
specset = SpecSetContext.new(text, self, nil)
|
65
|
+
specset.instance_eval(&blk)
|
66
|
+
@specs << specset
|
67
|
+
end
|
68
|
+
|
69
|
+
def hazard(hash)
|
70
|
+
text = hash.keys.first
|
71
|
+
expression = hash[text]
|
72
|
+
@hazards << Model::Hazard.new(text, expression)
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_model()
|
76
|
+
components = @components.map(&:to_model)
|
77
|
+
variables = Model::VariableSet.new(*@components.map(&:owned_variables).flatten())
|
78
|
+
specification = Model::Specification.new(@specs.map { |s| s.to_model(nil) })
|
79
|
+
Model::Graph.new(@name, components: components, variables: variables, specification: specification, hazards: @hazards)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# Require all module files
|
2
|
+
Dir[File.join(__dir__, "**", '*.rb')].sort.each { |file| require file }
|
3
|
+
|
4
|
+
module PgVerify
|
5
|
+
|
6
|
+
module Interpret
|
7
|
+
|
8
|
+
class InterpretError < PgVerify::Core::Error
|
9
|
+
attr_accessor :script_file
|
10
|
+
attr_accessor :line_number
|
11
|
+
attr_accessor :cause
|
12
|
+
|
13
|
+
def initialize(script_file, line_number, cause)
|
14
|
+
@script_file, @line_number, @cause = script_file, line_number, cause
|
15
|
+
end
|
16
|
+
|
17
|
+
def formatted()
|
18
|
+
title = "Error while interpreting script at #{@script_file}:#{@line_number}"
|
19
|
+
cause_str = format_cause()
|
20
|
+
|
21
|
+
sloc = Model::SourceLocation.new(@script_file, @line_number)
|
22
|
+
code_block = sloc.render_code_block
|
23
|
+
|
24
|
+
body = "#{code_block}\n\n#{cause_str}"
|
25
|
+
return title, body
|
26
|
+
end
|
27
|
+
|
28
|
+
def format_cause()
|
29
|
+
return "#{cause}" unless cause.is_a?(Core::Error)
|
30
|
+
return cause.to_formatted()
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class InvalidDSL_var < PgVerify::Core::Error
|
36
|
+
def initialize(message)
|
37
|
+
@message = message
|
38
|
+
end
|
39
|
+
def formatted()
|
40
|
+
hint = [ "Define variables like this:" ]
|
41
|
+
hint << "var <name>: <range> [, init: <condition>]"
|
42
|
+
hint << " where:"
|
43
|
+
hint << " <name> is the name of the variable"
|
44
|
+
hint << " <range> is a range of integers like (0..15) or an array of symbols like [ :yes, :no ]"
|
45
|
+
hint << " <condition> is either a value in that range or an expression"
|
46
|
+
return @message, nil, hint.join("\n")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class InvalidDSL_state < PgVerify::Core::Error
|
51
|
+
def initialize(message)
|
52
|
+
@message = message
|
53
|
+
end
|
54
|
+
def formatted()
|
55
|
+
hint = [ "Define states like this:" ]
|
56
|
+
hint << "states <states>"
|
57
|
+
hint << " where:"
|
58
|
+
hint << " <states> is an array of symbols like :on, :off"
|
59
|
+
return @message, nil, hint.join("\n")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class InvalidDSL_graph < PgVerify::Core::Error
|
64
|
+
def initialize(message)
|
65
|
+
@message = message
|
66
|
+
end
|
67
|
+
def formatted()
|
68
|
+
hint = [ "Define graphs like this:" ]
|
69
|
+
hint << "graph <name> do ... end"
|
70
|
+
return @message, nil, hint.join("\n")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class InvalidDSL_transition < PgVerify::Core::Error
|
75
|
+
def initialize(message)
|
76
|
+
@message = message
|
77
|
+
end
|
78
|
+
def formatted()
|
79
|
+
hint = [ "Define transitions like this:" ]
|
80
|
+
hint << "transition <pre-state> => <post-state> do ... end"
|
81
|
+
hint << " where"
|
82
|
+
hint << " <pre-state> & <post-state> are states in this graph, like :on or :off"
|
83
|
+
return @message, nil, hint.join("\n")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class InvalidDSL_expression < PgVerify::Core::Error
|
88
|
+
def initialize(type, message)
|
89
|
+
@type, @message = type, message
|
90
|
+
end
|
91
|
+
def formatted()
|
92
|
+
hint = [ "Define #{@type}s like this:" ]
|
93
|
+
hint << "#{@type} <expression>"
|
94
|
+
hint << " where"
|
95
|
+
hint << " <expression> is a valid expression as a string or symbol"
|
96
|
+
return @message, nil, hint.join("\n")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class InvalidDSL_model < PgVerify::Core::Error
|
101
|
+
def initialize(type, message)
|
102
|
+
@type, @message = type, message
|
103
|
+
end
|
104
|
+
def formatted()
|
105
|
+
hint = [ "Define models like this:" ]
|
106
|
+
hint << "model <name> do ... end"
|
107
|
+
hint << " where"
|
108
|
+
hint << " <name> is a string or symbol"
|
109
|
+
return @message, nil, hint.join("\n")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class NoSuchStateError < PgVerify::Core::Error
|
114
|
+
def initialize(state, component)
|
115
|
+
@state, @component = state, component
|
116
|
+
end
|
117
|
+
def formatted()
|
118
|
+
title = "No such state #{@state} in component #{@component.name}"
|
119
|
+
body = "The component #{@component.name.to_s.c_cmp} does not contain a state called #{@state.to_s.c_state}.\n"
|
120
|
+
body += "Available states are: #{@component.states.map(&:to_s).map(&:c_state).join(', ')}"
|
121
|
+
hint = "Make sure to define states before defining transitions."
|
122
|
+
return title, body, hint
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class NoSuchScriptError < Core::Error
|
127
|
+
def initialize(script_path)
|
128
|
+
@script_path = script_path
|
129
|
+
end
|
130
|
+
|
131
|
+
def formatted()
|
132
|
+
title = "Could not find script at #{@script_path}"
|
133
|
+
body = "No script file at #{@script_path.c_error}!"
|
134
|
+
hint = "Make sure to create a program graph script at \n#{File.expand_path(@script_path)}"
|
135
|
+
return title, body, hint
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
module PgVerify
|
3
|
+
module Interpret
|
4
|
+
|
5
|
+
class PgScript
|
6
|
+
|
7
|
+
# List of models which are defined in this script
|
8
|
+
attr_accessor :models
|
9
|
+
# The full path to the source script file
|
10
|
+
attr_accessor :script_file
|
11
|
+
|
12
|
+
|
13
|
+
def initialize()
|
14
|
+
@models = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def interpret(file, validate: true)
|
18
|
+
file = File.expand_path(file)
|
19
|
+
raise NoSuchScriptError.new(file) unless File.file?(file)
|
20
|
+
@script_file ||= file
|
21
|
+
|
22
|
+
begin
|
23
|
+
Dir.chdir(File.dirname(file)) { eval(File.read(file), get_binding(), file) }
|
24
|
+
rescue Exception => e
|
25
|
+
re_raise_exception(file, self, e)
|
26
|
+
end
|
27
|
+
|
28
|
+
@models.each { |model| Model::Validation.validate!(model) } if validate
|
29
|
+
|
30
|
+
return @models
|
31
|
+
end
|
32
|
+
|
33
|
+
def model(name, &blk)
|
34
|
+
raise InvalidDSL_model.new("Name '#{name}' is neither a symbol nor string") unless name.is_a?(Symbol) || name.is_a?(String)
|
35
|
+
graph_ctx = Interpret::GraphContext.new(name.to_sym, self)
|
36
|
+
graph_ctx.instance_eval(&blk)
|
37
|
+
@models << graph_ctx.to_model()
|
38
|
+
end
|
39
|
+
|
40
|
+
# Re-raise an exception which occurred during evaluation of a script to more user
|
41
|
+
# friendly error messages.
|
42
|
+
def re_raise_exception(file, script, ex)
|
43
|
+
# Determine the line in the script file where the error was raised
|
44
|
+
line_number = find_source_location(ex.backtrace).line_number
|
45
|
+
|
46
|
+
# Include the full backtrace in the message if enabled
|
47
|
+
if Settings.full_stack_trace
|
48
|
+
title = "Here is the full backtrace of the exception!".c_sidenote + "\n"
|
49
|
+
title += "(You don't have to bother with this unless you are developing pg-verify)".c_sidenote
|
50
|
+
bt = ex.backtrace.map(&:c_sidenote).join("\n").indented(str: ">> ".c_sidenote)
|
51
|
+
end
|
52
|
+
|
53
|
+
raise InterpretError.new(file, line_number, ex)
|
54
|
+
end
|
55
|
+
private :re_raise_exception
|
56
|
+
|
57
|
+
def find_source_location(trace = caller())
|
58
|
+
line_number = trace
|
59
|
+
.select { |l| l.include?(@script_file) }
|
60
|
+
.map { |l| l.split(":in")[0] }.reject(&:blank?)
|
61
|
+
.map { |l| l.split(":")[-1] }.reject(&:blank?)
|
62
|
+
.uniq.first.to_i
|
63
|
+
Model::SourceLocation.new(@script_file, line_number)
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_binding()
|
67
|
+
binding()
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module PgVerify
|
2
|
+
module Interpret
|
3
|
+
|
4
|
+
class LTLBuilder
|
5
|
+
|
6
|
+
def globally()
|
7
|
+
return LTLBuilderGlobally.new()
|
8
|
+
end
|
9
|
+
def after(expression)
|
10
|
+
return LTLBuilderAfter.new(expression)
|
11
|
+
end
|
12
|
+
def before(expression)
|
13
|
+
LTLBuilderBefore.new(expression)
|
14
|
+
end
|
15
|
+
def between(); end
|
16
|
+
def after_until(); end
|
17
|
+
|
18
|
+
class LTLBuilderBase
|
19
|
+
|
20
|
+
def build(pattern, map)
|
21
|
+
map.each { |key, val|
|
22
|
+
pattern = pattern.gsub(key.to_s, "#{val}")
|
23
|
+
}
|
24
|
+
return pattern
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class LTLBuilderGlobally < LTLBuilderBase
|
30
|
+
def global(p)
|
31
|
+
build "G p", { p: p }
|
32
|
+
end
|
33
|
+
def never(p)
|
34
|
+
build "G !( p )", { p: p }
|
35
|
+
end
|
36
|
+
def exists(p)
|
37
|
+
build "F p", { p: p }
|
38
|
+
end
|
39
|
+
def reacts(p, s)
|
40
|
+
build "G ( p => F s )", { p: p, s: s }
|
41
|
+
end
|
42
|
+
def precedes(p, s)
|
43
|
+
build "!(p) W s", { p: p, s: s }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class LTLBuilderAfter < LTLBuilderBase
|
48
|
+
attr_accessor :q
|
49
|
+
def initialize(q); @q = q end
|
50
|
+
def global(p)
|
51
|
+
build "G ( q -> G p )", { p: p, q: q }
|
52
|
+
end
|
53
|
+
def never(p)
|
54
|
+
build "G ( q -> G !(p))", { p: p, q: q }
|
55
|
+
end
|
56
|
+
def exists(p)
|
57
|
+
build "( G !(q) ) || ( F ( q && F p ) )", { p: p, q: q }
|
58
|
+
end
|
59
|
+
def reacts(p, s)
|
60
|
+
build "G ( q => G (p => F s) )", { p: p, q: q, s: s}
|
61
|
+
end
|
62
|
+
def precedes(p, s)
|
63
|
+
build "G !(q) || F ( q && !(p) W s )", { p: p, q: q, s: s}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class LTLBuilderBefore < LTLBuilderBase
|
68
|
+
attr_accessor :q
|
69
|
+
def initialize(q); @q = q end
|
70
|
+
def global(p)
|
71
|
+
|
72
|
+
end
|
73
|
+
def never(p)
|
74
|
+
build "( F q ) => ( !(p) U q )", { p: p, q: q }
|
75
|
+
end
|
76
|
+
def exists(p)
|
77
|
+
|
78
|
+
end
|
79
|
+
def reacts(p, s)
|
80
|
+
|
81
|
+
end
|
82
|
+
def precedes(p, s)
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PgVerify
|
2
|
+
module Interpret
|
3
|
+
|
4
|
+
class SpecContext
|
5
|
+
# The text of this spec as a string.
|
6
|
+
attr_accessor :text
|
7
|
+
|
8
|
+
# The LTL/CTL expression of this spec
|
9
|
+
attr_accessor :expression
|
10
|
+
|
11
|
+
# The parent set of this spec
|
12
|
+
attr_accessor :parent
|
13
|
+
|
14
|
+
attr_accessor :source_location
|
15
|
+
|
16
|
+
def initialize(text, expression, parent)
|
17
|
+
@text, @expression, @parent = text, expression, parent
|
18
|
+
@expression = Model::ParsedExpression.new(expression, Model::ParsedExpression::TYPE_TL)
|
19
|
+
@expression.source_location = parent.parent_graph.parent_script.find_source_location()
|
20
|
+
@source_location = parent.parent_graph.parent_script.find_source_location()
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_model(parent)
|
24
|
+
spec = Model::Spec.new(@text, @expression, parent)
|
25
|
+
spec.source_location = @source_location
|
26
|
+
return spec
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module PgVerify
|
2
|
+
module Interpret
|
3
|
+
|
4
|
+
class SpecSetContext
|
5
|
+
|
6
|
+
# The text of this spec set as a string.
|
7
|
+
attr_accessor :text
|
8
|
+
|
9
|
+
# The sub-spec sets contained in this spec set
|
10
|
+
attr_accessor :children
|
11
|
+
|
12
|
+
attr_accessor :parent
|
13
|
+
|
14
|
+
attr_accessor :assumption
|
15
|
+
|
16
|
+
attr_accessor :parent_graph
|
17
|
+
|
18
|
+
attr_accessor :source_location
|
19
|
+
|
20
|
+
def initialize(text, parent_graph, parent, children = [])
|
21
|
+
@text, @parent, @children = text, parent, children
|
22
|
+
@parent_graph = parent_graph
|
23
|
+
@source_location = parent_graph.parent_script.find_source_location()
|
24
|
+
end
|
25
|
+
|
26
|
+
def specify(text, &blk)
|
27
|
+
subset = SpecSetContext.new(text, @parent_graph, self)
|
28
|
+
subset.instance_eval(&blk)
|
29
|
+
children << subset
|
30
|
+
end
|
31
|
+
|
32
|
+
def assuming(hash, &blk)
|
33
|
+
assumption_text = hash.keys.first
|
34
|
+
assumption_expression = hash.values.first
|
35
|
+
subset = SpecSetContext.new("", @parent_graph, self)
|
36
|
+
subset.assumption = { text: assumption_text, expression: assumption_expression }
|
37
|
+
subset.instance_eval(&blk)
|
38
|
+
children << subset
|
39
|
+
end
|
40
|
+
|
41
|
+
def no_errors()
|
42
|
+
err_graphs = @parent_graph.components.select(&:represents_fault)
|
43
|
+
expression = err_graphs.map { |g| "#{g.name} == No" }.join(" && ")
|
44
|
+
return { "there are no errors" => "G ( #{expression} )" }
|
45
|
+
end
|
46
|
+
|
47
|
+
def it(hash)
|
48
|
+
# TODO: Handle errors
|
49
|
+
text = hash.keys.first
|
50
|
+
expression = hash[text]
|
51
|
+
children << SpecContext.new(text, expression.to_s, self)
|
52
|
+
end
|
53
|
+
|
54
|
+
def ltl()
|
55
|
+
return LTLBuilder.new()
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_model(parent)
|
59
|
+
model = Model::SpecSet.new(@text, @assumption, parent, [])
|
60
|
+
model.children = @children.map { |child| child.to_model(model) }
|
61
|
+
return model
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module PgVerify
|
2
|
+
module Interpret
|
3
|
+
|
4
|
+
class TransitionContext
|
5
|
+
|
6
|
+
TempExpression = Struct.new(:string, :source_location)
|
7
|
+
|
8
|
+
# Back-reference to the component which owns this transition
|
9
|
+
attr_accessor :parent_component
|
10
|
+
|
11
|
+
# Source and target states as symbols
|
12
|
+
attr_accessor :src_state, :tgt_state
|
13
|
+
|
14
|
+
# Strings for the guars, actions and preconditions
|
15
|
+
attr_accessor :guard, :action, :precon
|
16
|
+
|
17
|
+
# Source location for this transition
|
18
|
+
attr_accessor :source_location
|
19
|
+
|
20
|
+
def initialize(parent_component, src_state, tgt_state)
|
21
|
+
@parent_component = parent_component
|
22
|
+
@src_state, @tgt_state = src_state, tgt_state
|
23
|
+
@source_location = @parent_component.parent_graph.parent_script.find_source_location()
|
24
|
+
end
|
25
|
+
|
26
|
+
def guard(str)
|
27
|
+
raise "Guard was already defined!" unless @guard.nil?
|
28
|
+
raise InvalidDSL_expression.new("guard", "Expression is neither a sting, nor symbol: #{str}") unless str.is_a?(String) | str.is_a?(Symbol)
|
29
|
+
@guard = Model::ParsedExpression.new(str, Model::ParsedExpression::TYPE_GUARD)
|
30
|
+
@guard.source_location = @parent_component.parent_graph.parent_script.find_source_location()
|
31
|
+
end
|
32
|
+
|
33
|
+
def action(str)
|
34
|
+
raise "Action was already defined!" unless @action.nil?
|
35
|
+
raise InvalidDSL_expression.new("action", "Expression is neither a sting, nor symbol: #{str}") unless str.is_a?(String) | str.is_a?(Symbol)
|
36
|
+
@action = Model::ParsedExpression.new(str, Model::ParsedExpression::TYPE_ACTION)
|
37
|
+
@action.source_location = @parent_component.parent_graph.parent_script.find_source_location()
|
38
|
+
end
|
39
|
+
|
40
|
+
def precon(str)
|
41
|
+
raise "Precondition was already defined!" unless @guard.nil?
|
42
|
+
raise InvalidDSL_expression.new("precon", "Expression is neither a sting, nor symbol: #{str}") unless str.is_a?(String) | str.is_a?(Symbol)
|
43
|
+
@precon = Model::ParsedExpression.new(str, Model::ParsedExpression::TYPE_GUARD)
|
44
|
+
@precon.source_location = @parent_component.parent_graph.parent_script.find_source_location()
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_model()
|
48
|
+
Model::Transition.new(@src_state, @tgt_state, action: @action, precon: @precon, guard: @guard)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PgVerify
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class AllocationSet
|
5
|
+
|
6
|
+
# An array of variables
|
7
|
+
attr_accessor :variables
|
8
|
+
|
9
|
+
# An array of allocations for those variables.
|
10
|
+
# Each allocation is a an array like [0, 1] where indices match the variable array
|
11
|
+
# to represent values for that variable
|
12
|
+
attr_accessor :allocations
|
13
|
+
|
14
|
+
def initialize(variables, allocations)
|
15
|
+
if !allocations.empty? && variables.length != allocations.first.length
|
16
|
+
raise "Variables and allocations must match in length!"
|
17
|
+
end
|
18
|
+
@variables, @allocations = variables, allocations
|
19
|
+
end
|
20
|
+
|
21
|
+
def length()
|
22
|
+
allocations.length
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module PgVerify
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Assignment
|
6
|
+
|
7
|
+
ASSIGN_OPERATOR = ':='
|
8
|
+
|
9
|
+
# The name of the variable which is assigned a value
|
10
|
+
attr_accessor :assigned_variable
|
11
|
+
|
12
|
+
# The Model::Expression which of this assignment
|
13
|
+
attr_accessor :expression
|
14
|
+
|
15
|
+
def self.from_string(string)
|
16
|
+
raise "Not an assignment: #{string}" unless string.include?(ASSIGN_OPERATOR)
|
17
|
+
split = string.split(ASSIGN_OPERATOR).map(&:strip)
|
18
|
+
assigned_variable = split[0].to_sym
|
19
|
+
expression = Expression.from_string(split[1])
|
20
|
+
return Assignment.new(assigned_variable, expression)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(assigned_variable, expression)
|
24
|
+
@assigned_variable, @expression = assigned_variable, expression
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s()
|
28
|
+
return "#{assigned_variable} #{ASSIGN_OPERATOR} #{expression.to_s}"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module PgVerify
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Component
|
6
|
+
|
7
|
+
# The name of this component as a symbol
|
8
|
+
attr_accessor :name
|
9
|
+
# A list of symbols representing states
|
10
|
+
attr_accessor :states
|
11
|
+
# A list of transitions between states
|
12
|
+
attr_accessor :transitions
|
13
|
+
# Flag on whether this component is used to represent a fault.
|
14
|
+
attr_accessor :represents_fault
|
15
|
+
# The source location of this components definition
|
16
|
+
attr_accessor :source_location
|
17
|
+
|
18
|
+
# A Model::ParsedExpression used as the init expression.
|
19
|
+
# This expression must hold true initially. It can be used
|
20
|
+
# to restrict or set the initial state and variables.
|
21
|
+
# Can be left as nil in order to not restrict anything
|
22
|
+
attr_accessor :init_expression
|
23
|
+
|
24
|
+
def initialize(args = {})
|
25
|
+
@name = ( args[:name] || raise('Blubb') )
|
26
|
+
@states = ( args[:states] || [] )
|
27
|
+
@transitions = ( args[:transitions] || [] )
|
28
|
+
@represents_fault = args[:represents_fault].nil? ? false : args[:represents_fault]
|
29
|
+
@source_location = ( args[:source_location] )
|
30
|
+
@init_expression = args[:init_expression]
|
31
|
+
end
|
32
|
+
|
33
|
+
def represents_fault?()
|
34
|
+
return @represents_fault
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|