pg-verify 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -20
- data/README.md +1 -1
- data/data/banner.txt +5 -0
- data/data/project-template/README.md +81 -0
- data/doc/examples/vending_machine/checkpoint_1.rb +29 -0
- data/doc/examples/vending_machine/checkpoint_2.rb +47 -0
- data/doc/examples/vending_machine/checkpoint_3.rb +68 -0
- data/doc/examples/vending_machine/checkpoint_4.rb +202 -0
- data/integration_tests/ruby_dsl/001_states.rb +2 -1
- data/integration_tests/ruby_dsl/002_transitions.rb +2 -1
- data/integration_tests/ruby_dsl/017_ctl_specifications.rb +19 -0
- data/integration_tests/ruby_dsl/018_non_atomic_hazard.rb +17 -0
- data/integration_tests/ruby_dsl/019_multiple_actions.rb +21 -0
- data/integration_tests/ruby_dsl/020_vending_machine.rb +188 -0
- data/lib/pg-verify/cli/cli.rb +94 -24
- data/lib/pg-verify/cli/cli_utils.rb +61 -0
- data/lib/pg-verify/interpret/component_context.rb +1 -1
- data/lib/pg-verify/interpret/interpret.rb +1 -1
- data/lib/pg-verify/interpret/pg_script.rb +1 -0
- data/lib/pg-verify/model/parsed_expression.rb +10 -5
- data/lib/pg-verify/model/simulation/trace.rb +15 -4
- data/lib/pg-verify/model/validation/errors.rb +21 -2
- data/lib/pg-verify/nusmv/runner.rb +69 -17
- data/lib/pg-verify/puml/puml.rb +1 -0
- data/lib/pg-verify/transform/hash_transformation.rb +46 -14
- data/lib/pg-verify/transform/nusmv_transformation.rb +4 -3
- data/lib/pg-verify/transform/puml_transformation.rb +27 -8
- data/lib/pg-verify/version.rb +1 -1
- data/pg-verify.gemspec +0 -1
- data/vscript.rb +64 -0
- metadata +14 -18
- data/data/project-template/program-graph.rb.resource +0 -103
- /data/{data/project-template/.pg-verify.yml → lib/pg-verify/puml/runner.rb} +0 -0
@@ -5,9 +5,17 @@ module PgVerify
|
|
5
5
|
class Runner
|
6
6
|
|
7
7
|
def run_specs(program_graph)
|
8
|
-
|
9
|
-
|
8
|
+
transformer = Transform::NuSmvTransformation.new
|
9
|
+
nusmv_s = transformer.transform_graph(program_graph, include_specs: false)
|
10
|
+
commands = [ "go" ]
|
10
11
|
specs = program_graph.specification.flatten()
|
12
|
+
transform_map = specs.each { |spec|
|
13
|
+
transformed_spec_expr = transformer.transform_expression(spec.expression, program_graph.all_variables)
|
14
|
+
cmd = { ltl: "check_ltlspec", ctl: "check_ctlspec" }[spec.expression.predict_type]
|
15
|
+
commands << "#{cmd} -p \"#{transformed_spec_expr}\""
|
16
|
+
}
|
17
|
+
output = eval_nusmv(nusmv_s, commands: commands)
|
18
|
+
|
11
19
|
return parse_spec_results(program_graph, specs, output)
|
12
20
|
end
|
13
21
|
|
@@ -20,8 +28,8 @@ module PgVerify
|
|
20
28
|
nusmv_output.split("\n").each { |line|
|
21
29
|
result = line[/-- specification .* is (true|false)/, 1]
|
22
30
|
if !result.nil?
|
23
|
-
blocks << current_block unless current_block.nil?
|
24
|
-
current_block = block.new(result == "true", [])
|
31
|
+
blocks << current_block unless current_block.nil? # Complete the last block
|
32
|
+
current_block = block.new(result == "true", []) # Start a new block
|
25
33
|
next
|
26
34
|
end
|
27
35
|
current_block.lines << line unless current_block.nil?
|
@@ -41,10 +49,7 @@ module PgVerify
|
|
41
49
|
# variable to the value of that variable in that state
|
42
50
|
def run_simulation(program_graph, steps, random: false)
|
43
51
|
commands = []
|
44
|
-
commands << "
|
45
|
-
commands << "flatten_hierarchy"
|
46
|
-
commands << "encode_variables"
|
47
|
-
commands << "build_model"
|
52
|
+
commands << "go"
|
48
53
|
commands << "pick_state #{random ? '-r' : ''}"
|
49
54
|
commands << "simulate -k #{steps.to_s.to_i} -v #{random ? '-r' : ''}"
|
50
55
|
commands << "quit"
|
@@ -53,12 +58,48 @@ module PgVerify
|
|
53
58
|
return parse_trace(program_graph, output)
|
54
59
|
end
|
55
60
|
|
61
|
+
def run_check!(program_graph)
|
62
|
+
deadlock_state = run_check(program_graph)
|
63
|
+
return if deadlock_state.nil?
|
64
|
+
raise Model::Validation::DeadlockInFSMError.new(program_graph, deadlock_state)
|
65
|
+
end
|
66
|
+
|
67
|
+
def run_check(program_graph)
|
68
|
+
commands = [ "go", "check_fsm", "quit" ]
|
69
|
+
nusmv_s = Transform::NuSmvTransformation.new.transform_graph(program_graph, include_specs: false)
|
70
|
+
output = eval_nusmv(nusmv_s, commands: commands)
|
71
|
+
|
72
|
+
# Return "ok" if the FSM has no deadlocks
|
73
|
+
return nil if output.include?("The transition relation is total: No deadlock state exists")
|
74
|
+
|
75
|
+
# Otherwise compute and return the deadlock state
|
76
|
+
lines = output.split("\n").drop_while { |str|
|
77
|
+
!(str.start_with?("A deadlock state is:") || str.start_with?("successors is:"))
|
78
|
+
}
|
79
|
+
lines = lines[1, lines.length - 1]
|
80
|
+
|
81
|
+
var_state = {}
|
82
|
+
lines.each { |line|
|
83
|
+
key, val = parse_var_assignment_in_trace(line)
|
84
|
+
next if key.nil? || val.nil?
|
85
|
+
var_state[key] = val
|
86
|
+
}
|
87
|
+
|
88
|
+
return var_state
|
89
|
+
end
|
90
|
+
|
56
91
|
def parse_trace(program_graph, nusmv_output)
|
57
92
|
var_states, current_var_state = [], nil
|
58
93
|
|
59
94
|
loop_index = -1
|
60
95
|
|
61
96
|
nusmv_output.split("\n").each { |line|
|
97
|
+
# Mark a loop for the following state
|
98
|
+
if line.include?("Loop starts here")
|
99
|
+
loop_index = var_states.length
|
100
|
+
next
|
101
|
+
end
|
102
|
+
|
62
103
|
# Wait for heading of new state
|
63
104
|
if line.match(/\s*-> State: .+ <-/)
|
64
105
|
# Complete and store the old state if any
|
@@ -76,17 +117,28 @@ module PgVerify
|
|
76
117
|
# Skip lines before the first state
|
77
118
|
next if current_var_state.nil?
|
78
119
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
key_val = line.split("=").map(&:strip)
|
85
|
-
key = key_val[0].gsub("v.V_", "").to_sym
|
86
|
-
val = key_val[1].gsub("L_", "")
|
120
|
+
# Parse the variable state
|
121
|
+
key, val = parse_var_assignment_in_trace(line)
|
122
|
+
next if key.nil? || val.nil?
|
87
123
|
current_var_state[key] = val
|
88
124
|
}
|
89
|
-
|
125
|
+
# Finish up
|
126
|
+
unless current_var_state.nil?
|
127
|
+
missing_keys = var_states.empty? ? [] : var_states[-1].keys - current_var_state.keys
|
128
|
+
missing_keys.each { |key|
|
129
|
+
current_var_state[key] = var_states[-1][key]
|
130
|
+
}
|
131
|
+
var_states << current_var_state
|
132
|
+
end
|
133
|
+
return Model::Trace.new(program_graph, var_states, loop_index: loop_index)
|
134
|
+
end
|
135
|
+
|
136
|
+
def parse_var_assignment_in_trace(line)
|
137
|
+
return nil, nil unless line.include?("=")
|
138
|
+
key_val = line.split("=").map(&:strip)
|
139
|
+
key = key_val[0].gsub("v.V_", "").to_sym
|
140
|
+
val = key_val[1].gsub("L_", "")
|
141
|
+
return key, val
|
90
142
|
end
|
91
143
|
|
92
144
|
def eval_nusmv(nusmv_string, commands: [])
|
data/lib/pg-verify/puml/puml.rb
CHANGED
@@ -15,6 +15,7 @@ module PgVerify
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.convert_file(in_path, out_path)
|
18
|
+
# CALL WITH java -Djava.awt.headless=true -jar addon/plantuml-1.2023.13.jar --help
|
18
19
|
# TODO: The PlantUML jar switches focus to the desktop is if it would
|
19
20
|
# attempt to open a window each time it is invoked.
|
20
21
|
end
|
@@ -4,16 +4,24 @@ module PgVerify
|
|
4
4
|
class HashTransformation
|
5
5
|
|
6
6
|
def transform_graph(graph)
|
7
|
-
|
8
|
-
|
9
|
-
}
|
7
|
+
components = graph.components.map { |c| transform_component(graph, c) }
|
8
|
+
hazards = graph.hazards.map { |h| transform_hazard(h) }
|
9
|
+
sub_hash = {}
|
10
|
+
sub_hash["components"] = components
|
11
|
+
sub_hash["hazards"] = hazards unless hazards.empty?
|
12
|
+
sub_hash["specification"] = transform_specification(graph.specification)
|
13
|
+
return { graph.name.to_s => sub_hash }
|
10
14
|
end
|
11
15
|
|
12
16
|
def parse_graph(hash)
|
13
17
|
name = hash.keys.first.to_sym
|
14
18
|
graph = Model::Graph.new(name)
|
15
|
-
components = hash[hash.keys.first].map { |c| parse_component(graph, c) }
|
19
|
+
components = hash[hash.keys.first]["components"].map { |c| parse_component(graph, c) }
|
20
|
+
hazards = hash[hash.keys.first]["hazards"]&.map { |h| parse_hazard(h) } || []
|
21
|
+
specification = parse_specification(hash[hash.keys.first]["specification"])
|
16
22
|
graph.components = components
|
23
|
+
graph.hazards = hazards
|
24
|
+
graph.specification = specification
|
17
25
|
return graph
|
18
26
|
end
|
19
27
|
|
@@ -22,6 +30,7 @@ module PgVerify
|
|
22
30
|
return { component.name.to_s => {
|
23
31
|
"states" => component.states.map(&:to_s),
|
24
32
|
"variables" => variables.map { |v| transform_variable(v) },
|
33
|
+
"init" => transform_expression(component.init_expression),
|
25
34
|
"transitions" => component.transitions.map { |t| transform_transition(t) },
|
26
35
|
"represents_fault" => component.represents_fault
|
27
36
|
}}
|
@@ -31,13 +40,14 @@ module PgVerify
|
|
31
40
|
name = hash.keys.first
|
32
41
|
states = hash[name]["states"].map(&:to_sym)
|
33
42
|
variables = hash[name]["variables"].map { |v| parse_variable(name, v) }
|
43
|
+
init_expression = parse_expression(hash[name]["init"])
|
34
44
|
transitions = hash[name]["transitions"].map { |t| parse_transition(t) }
|
35
45
|
represents_fault = hash[name]["represents_fault"]
|
36
46
|
|
37
47
|
graph.variables += variables
|
38
48
|
|
39
49
|
return Model::Component.new(name: name.to_sym, states: states,
|
40
|
-
transitions: transitions, represents_fault: represents_fault)
|
50
|
+
transitions: transitions, represents_fault: represents_fault, init_expression: init_expression)
|
41
51
|
end
|
42
52
|
|
43
53
|
def transform_variable(variable)
|
@@ -49,7 +59,7 @@ module PgVerify
|
|
49
59
|
|
50
60
|
def parse_variable(owner_name, hash)
|
51
61
|
name = hash.keys.first
|
52
|
-
range = hash[name]["range"]
|
62
|
+
range = parse_variable_range(hash[name]["range"])
|
53
63
|
init_expression = parse_expression(hash[name]["init"])
|
54
64
|
return Model::Variable.new(name.to_sym, range, owner_name.to_sym, nil, init: init_expression)
|
55
65
|
end
|
@@ -60,9 +70,10 @@ module PgVerify
|
|
60
70
|
end
|
61
71
|
|
62
72
|
def parse_variable_range(range)
|
63
|
-
|
73
|
+
# Matches ranges like 1..12 or -1..23
|
74
|
+
if range.is_a?(String) && range.match(/\A\-?\d+\.\.\-?\d+\z/)
|
64
75
|
first, last = range.split("..")[0], range.split("..")[-1]
|
65
|
-
return Range.new(first, last)
|
76
|
+
return Range.new(first.to_i, last.to_i)
|
66
77
|
end
|
67
78
|
return range
|
68
79
|
end
|
@@ -85,20 +96,41 @@ module PgVerify
|
|
85
96
|
def transform_transition(transition)
|
86
97
|
name = "#{transition.src_state} -> #{transition.tgt_state}"
|
87
98
|
return { name => {
|
88
|
-
"precon" => transition.precon
|
89
|
-
"guard" => transition.guard
|
90
|
-
"action" => transition.action
|
99
|
+
"precon" => transform_expression(transition.precon),
|
100
|
+
"guard" => transform_expression(transition.guard),
|
101
|
+
"action" => transform_expression(transition.action),
|
91
102
|
}}
|
92
103
|
end
|
93
104
|
|
94
105
|
def parse_transition(hash)
|
95
106
|
src_state, tgt_state = hash.keys.first.split("->").map(&:strip).map(&:to_sym)
|
96
|
-
precon = hash.values.first["precon"]
|
97
|
-
guard = hash.values.first["guard"]
|
98
|
-
action = hash.values.first["action"]
|
107
|
+
precon = parse_expression(hash.values.first["precon"])
|
108
|
+
guard = parse_expression(hash.values.first["guard"] )
|
109
|
+
action = parse_expression(hash.values.first["action"])
|
99
110
|
return Model::Transition.new(src_state, tgt_state, precon: precon, guard: guard, action: action)
|
100
111
|
end
|
101
112
|
|
113
|
+
def transform_hazard(hazard)
|
114
|
+
return { "label" => hazard.text, "expression" => hazard.expression }
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_hazard(hash)
|
118
|
+
return Model::Hazard.new(hash["label"], hash["expression"])
|
119
|
+
end
|
120
|
+
|
121
|
+
def transform_specification(specification)
|
122
|
+
return specification.flatten().map { |spec|
|
123
|
+
{ "label" => spec.text.strip, "expression" => transform_expression(spec.expression) }
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse_specification(array)
|
128
|
+
specs = array.map { |hash|
|
129
|
+
Model::Spec.new(hash["label"], parse_expression(hash["expression"]), nil)
|
130
|
+
}
|
131
|
+
return Model::Specification.new([Model::SpecSet.wrap(specs)])
|
132
|
+
end
|
133
|
+
|
102
134
|
end
|
103
135
|
end
|
104
136
|
end
|
@@ -3,7 +3,7 @@ module PgVerify
|
|
3
3
|
|
4
4
|
class NuSmvTransformation
|
5
5
|
|
6
|
-
def transform_graph(program_graph)
|
6
|
+
def transform_graph(program_graph, include_specs: true)
|
7
7
|
variables = program_graph.all_variables
|
8
8
|
components = program_graph.components
|
9
9
|
|
@@ -11,7 +11,7 @@ module PgVerify
|
|
11
11
|
cmp_s = transform_components(components, variables)
|
12
12
|
main_s = transform_main_module(components)
|
13
13
|
|
14
|
-
specs = transform_specification(program_graph.specification, variables)
|
14
|
+
specs = transform_specification(program_graph.specification, variables) if include_specs
|
15
15
|
|
16
16
|
return "#{var_s}\n\n#{cmp_s}\n\n#{main_s}\n\n#{specs}\n"
|
17
17
|
end
|
@@ -251,7 +251,8 @@ module PgVerify
|
|
251
251
|
def transform_spec(spec, varset)
|
252
252
|
expression_s = "-- #{spec.text}\n"
|
253
253
|
expression_s += "-- Defined in: #{spec.source_location.to_s}\n"
|
254
|
-
|
254
|
+
keyword = { ltl: "LTLSPEC", ctl: "CTLSPEC" }[spec.expression.predict_type]
|
255
|
+
expression_s += keyword + " " + transform_expression(spec.expression, varset)
|
255
256
|
return expression_s
|
256
257
|
end
|
257
258
|
|
@@ -5,13 +5,25 @@ module PgVerify
|
|
5
5
|
|
6
6
|
class PumlTransformation
|
7
7
|
|
8
|
+
attr_accessor :render_labels
|
9
|
+
attr_accessor :render_precons
|
10
|
+
attr_accessor :render_guards
|
11
|
+
attr_accessor :render_actions
|
12
|
+
|
13
|
+
def initialize(render_options = {})
|
14
|
+
@render_labels = render_options[:render_labels].nil? ? true : render_options[:render_labels]
|
15
|
+
@render_precons = render_options[:render_precons].nil? ? true : render_options[:render_precons]
|
16
|
+
@render_guards = render_options[:render_guards].nil? ? true : render_options[:render_guards]
|
17
|
+
@render_actions = render_options[:render_actions].nil? ? true : render_options[:render_actions]
|
18
|
+
end
|
19
|
+
|
8
20
|
def transform_graph(graph, variable_state: nil, only: nil)
|
9
21
|
|
10
22
|
components = graph.components
|
11
23
|
components = components.select { |c| only.map(&:to_s).include?(c.name.to_s) } unless only.nil?
|
12
24
|
|
13
25
|
parts = []
|
14
|
-
parts << components.map { |c| transform_component(graph, c) }.join("\n\n")
|
26
|
+
parts << components.map { |c| transform_component(graph, c, variable_state) }.join("\n\n")
|
15
27
|
parts << transform_variable_state(graph, variable_state) unless variable_state.nil?
|
16
28
|
parts = parts.compact.join("\n\n")
|
17
29
|
return "@startuml Programmgraph\n#{parts}\n@enduml\n"
|
@@ -25,14 +37,14 @@ module PgVerify
|
|
25
37
|
}.join("\n")
|
26
38
|
end
|
27
39
|
|
28
|
-
def transform_component(graph, component)
|
40
|
+
def transform_component(graph, component, variable_state)
|
29
41
|
# Transform component states
|
30
42
|
states_s = component.states.map { |s| transform_state(component, s) }.join("\n")
|
31
43
|
# Transform component transitions
|
32
44
|
trans_s = component.transitions.map { |t| transform_transition(component, t) }.join("\n")
|
33
45
|
# Transform component variables
|
34
46
|
vars = graph.variables.select_by_owner(component.name)
|
35
|
-
vars_s = transform_variables(component, vars)
|
47
|
+
vars_s = transform_variables(component, vars, variable_state)
|
36
48
|
# Transform the initial state
|
37
49
|
initial_s = transform_initial(graph, component)
|
38
50
|
|
@@ -59,10 +71,14 @@ module PgVerify
|
|
59
71
|
end
|
60
72
|
|
61
73
|
def transform_transition(component, transition)
|
62
|
-
|
63
|
-
|
74
|
+
precon = @render_precons ? transition.precon : nil
|
75
|
+
guard = @render_guards ? transition.guard : nil
|
76
|
+
action = @render_actions ? transition.action : nil
|
64
77
|
|
78
|
+
label = [ precon, guard ].map(&:to_s).reject(&:empty?).join(" && ")
|
79
|
+
label += " / " + action.to_s unless action.nil?
|
65
80
|
label = ": #{label}" unless label.strip.empty?
|
81
|
+
label = "" unless @render_labels
|
66
82
|
return "#{component.name}_#{transition.src_state} --> #{component.name}_#{transition.tgt_state} #{label}"
|
67
83
|
end
|
68
84
|
|
@@ -70,9 +86,12 @@ module PgVerify
|
|
70
86
|
str.split("\n").map { |s| "\t#{s}" }.join("\n")
|
71
87
|
end
|
72
88
|
|
73
|
-
def transform_variables(component, variables)
|
74
|
-
return nil if variables.empty?
|
75
|
-
body = variables.map { |
|
89
|
+
def transform_variables(component, variables, variable_state)
|
90
|
+
return nil if variables.empty?
|
91
|
+
body = variables.map { |var|
|
92
|
+
value = variable_state.nil? ? transform_range(var.range) : variable_state[var.name]
|
93
|
+
"#{var.name} => #{value}"
|
94
|
+
}.join("\n")
|
76
95
|
return "map #{component.name}Variables {\n#{body}\n}"
|
77
96
|
end
|
78
97
|
|
data/lib/pg-verify/version.rb
CHANGED
data/pg-verify.gemspec
CHANGED
@@ -30,7 +30,6 @@ Gem::Specification.new do |spec|
|
|
30
30
|
|
31
31
|
# Gem dependencies
|
32
32
|
spec.add_dependency "thor", "~> 1.2.1"
|
33
|
-
spec.add_dependency "ebnf", "~> 2.3.4"
|
34
33
|
spec.add_dependency "rainbow", "~> 3.0.0"
|
35
34
|
spec.add_dependency "config", "~> 4.2.1"
|
36
35
|
spec.add_dependency "plantuml_builder", "~> 0.3.0"
|
data/vscript.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
task :gen_examples do
|
3
|
+
files = Dir["integration_tests/ruby_dsl/*.rb"].select(&:file?)
|
4
|
+
|
5
|
+
target_dir = "examples"
|
6
|
+
FileUtils.rm_rf(target_dir); FileUtils.mkdir(target_dir)
|
7
|
+
|
8
|
+
files.each { |file|
|
9
|
+
json = x "./devpg show json --script #{file}", verbose: false
|
10
|
+
yaml = x "./devpg show yaml --script #{file}", verbose: false
|
11
|
+
json_file = File.join(target_dir, File.basename(file, '.rb') + ".json")
|
12
|
+
yaml_file = File.join(target_dir, File.basename(file, '.rb') + ".yaml")
|
13
|
+
FileUtils.cp(file, File.join(target_dir, File.basename(file)))
|
14
|
+
File.write(json_file, json)
|
15
|
+
File.write(yaml_file, yaml)
|
16
|
+
|
17
|
+
x "tar -czf examples.tar.gz examples/"
|
18
|
+
}
|
19
|
+
FileUtils.rm_rf(target_dir)
|
20
|
+
|
21
|
+
done "Generated examples.tar.gz"
|
22
|
+
end
|
23
|
+
|
24
|
+
task :integration_test do
|
25
|
+
|
26
|
+
work_dir = ".pg-work"
|
27
|
+
FileUtils.rm_rf(work_dir); FileUtils.mkdir(work_dir)
|
28
|
+
|
29
|
+
test_files = Dir["integration_tests/ruby_dsl/*.rb"].sort.select(&:file?)
|
30
|
+
|
31
|
+
|
32
|
+
# Create json files for all tests
|
33
|
+
test_files.each { |source|
|
34
|
+
log "JSON #{File.basename(source)}"
|
35
|
+
target = File.join(work_dir, File.basename(source, '.rb') + ".json")
|
36
|
+
content = x "./devpg show json --script #{source}", verbose: false
|
37
|
+
File.write(target, content)
|
38
|
+
}
|
39
|
+
|
40
|
+
# Create json files for all tests
|
41
|
+
test_files.each { |source|
|
42
|
+
log "YAML #{File.basename(source)}"
|
43
|
+
target = File.join(work_dir, File.basename(source, '.rb') + ".yaml")
|
44
|
+
content = x "./devpg show yaml --script #{source}", verbose: false
|
45
|
+
File.write(target, content)
|
46
|
+
}
|
47
|
+
|
48
|
+
# Run tests for all files
|
49
|
+
test_files.each { |source|
|
50
|
+
args = {
|
51
|
+
"script" => source,
|
52
|
+
"json-file" => File.join(work_dir, File.basename(source, '.rb') + ".json"),
|
53
|
+
}
|
54
|
+
commands = [ "show puml", "test", "dcca", "show png", "simulate" ]
|
55
|
+
args.each { |argument, file|
|
56
|
+
commands.each { |command|
|
57
|
+
log "pgv #{command} #{argument} | #{File.basename(file)}"
|
58
|
+
x "./devpg #{command} --#{argument} #{file}", verbose: false
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
done "Great success!"
|
64
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg-verify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arian Weber
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.2.1
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: ebnf
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 2.3.4
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 2.3.4
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rainbow
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -97,16 +83,19 @@ files:
|
|
97
83
|
- bin/pg-verify.rb
|
98
84
|
- bin/setup
|
99
85
|
- calc.ebnf
|
86
|
+
- data/banner.txt
|
100
87
|
- data/config/pg-verify.yml
|
101
88
|
- data/nusmv.sample.smv
|
102
89
|
- data/project-template/.gitignore.resource
|
103
|
-
- data/project-template/.pg-verify.yml
|
104
90
|
- data/project-template/README.md
|
105
91
|
- data/project-template/addon/.keep
|
106
|
-
- data/project-template/program-graph.rb.resource
|
107
92
|
- devpg
|
108
93
|
- doc/examples/railroad_crossing.rb
|
109
94
|
- doc/examples/train-tree.rb
|
95
|
+
- doc/examples/vending_machine/checkpoint_1.rb
|
96
|
+
- doc/examples/vending_machine/checkpoint_2.rb
|
97
|
+
- doc/examples/vending_machine/checkpoint_3.rb
|
98
|
+
- doc/examples/vending_machine/checkpoint_4.rb
|
110
99
|
- doc/examples/weidezaun.rb
|
111
100
|
- doc/examples/weidezaun.txt
|
112
101
|
- doc/expose/definition.png
|
@@ -130,8 +119,13 @@ files:
|
|
130
119
|
- integration_tests/ruby_dsl/014_tau_transitions.rb
|
131
120
|
- integration_tests/ruby_dsl/015_basic_dcca.rb
|
132
121
|
- integration_tests/ruby_dsl/016_pressure_tank.rb
|
122
|
+
- integration_tests/ruby_dsl/017_ctl_specifications.rb
|
123
|
+
- integration_tests/ruby_dsl/018_non_atomic_hazard.rb
|
124
|
+
- integration_tests/ruby_dsl/019_multiple_actions.rb
|
125
|
+
- integration_tests/ruby_dsl/020_vending_machine.rb
|
133
126
|
- lib/pg-verify.rb
|
134
127
|
- lib/pg-verify/cli/cli.rb
|
128
|
+
- lib/pg-verify/cli/cli_utils.rb
|
135
129
|
- lib/pg-verify/core/cmd_runner.rb
|
136
130
|
- lib/pg-verify/core/core.rb
|
137
131
|
- lib/pg-verify/core/extensions/array_extensions.rb
|
@@ -185,6 +179,7 @@ files:
|
|
185
179
|
- lib/pg-verify/nusmv/nusmv.rb
|
186
180
|
- lib/pg-verify/nusmv/runner.rb
|
187
181
|
- lib/pg-verify/puml/puml.rb
|
182
|
+
- lib/pg-verify/puml/runner.rb
|
188
183
|
- lib/pg-verify/shell/loading/line_animation.rb
|
189
184
|
- lib/pg-verify/shell/loading/loading_animation.rb
|
190
185
|
- lib/pg-verify/shell/loading/loading_prompt.rb
|
@@ -200,6 +195,7 @@ files:
|
|
200
195
|
- lib/pg-verify/version.rb
|
201
196
|
- pg-verify.gemspec
|
202
197
|
- sig/pg-verify.rbs
|
198
|
+
- vscript.rb
|
203
199
|
homepage: https://github.com/ArianWeber/pg-verify
|
204
200
|
licenses: []
|
205
201
|
metadata:
|
@@ -1,103 +0,0 @@
|
|
1
|
-
####################################################################
|
2
|
-
# Model definition
|
3
|
-
####################################################################
|
4
|
-
|
5
|
-
model :FeelThePain do
|
6
|
-
|
7
|
-
# Define a graph called 'Hand'.
|
8
|
-
graph :Hand do
|
9
|
-
# The hand can be touching the fence or be somewhere else
|
10
|
-
# It starts in 'somewhere' as that state is listed first
|
11
|
-
states :somewhere, :at_fence
|
12
|
-
|
13
|
-
# Transition non-deterministically between those states
|
14
|
-
transition :somewhere => :somewhere
|
15
|
-
transition :somewhere => :at_fence
|
16
|
-
transition :at_fence => :somewhere
|
17
|
-
transition :at_fence => :at_fence
|
18
|
-
end
|
19
|
-
|
20
|
-
# Define another graph called 'PowerSwitch', which also
|
21
|
-
# transitions non-deterministically.
|
22
|
-
graph :PowerSwitch do
|
23
|
-
states :off, :on
|
24
|
-
transition :off => :off
|
25
|
-
transition :off => :on
|
26
|
-
transition :on => :off
|
27
|
-
transition :on => :on
|
28
|
-
end
|
29
|
-
|
30
|
-
graph :Fence do
|
31
|
-
# The fence has no states we are interested in
|
32
|
-
states :exists
|
33
|
-
|
34
|
-
# The fence has a voltage which can go up to 15
|
35
|
-
var :voltage => (0..12), init: 0
|
36
|
-
|
37
|
-
# The voltage increases when the power switch is on
|
38
|
-
transition :exists => :exists do
|
39
|
-
guard "PowerSwitch == on"
|
40
|
-
action "voltage := voltage + 1"
|
41
|
-
end
|
42
|
-
# ..and instantly stops when the switch is off
|
43
|
-
transition :exists => :exists do
|
44
|
-
guard "PowerSwitch == off"
|
45
|
-
action "voltage := 0"
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
graph :Pain do
|
51
|
-
# We can either be in pain or not
|
52
|
-
states :No, :Yes
|
53
|
-
# Using regular variables with string interpolation
|
54
|
-
pain_threshold = 7
|
55
|
-
transition :No => :Yes do
|
56
|
-
# action "voltage := 2"
|
57
|
-
guard "Hand == at_fence && voltage >= #{pain_threshold}"
|
58
|
-
end
|
59
|
-
transition :Yes => :No do
|
60
|
-
guard "Hand == somewhere || voltage < #{pain_threshold}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
####################################################################
|
65
|
-
# Validity tests
|
66
|
-
####################################################################
|
67
|
-
|
68
|
-
# Specification of validity characteristics regarding the hand.
|
69
|
-
# The 'specify' block serves as a namespace/container for the contained specifications
|
70
|
-
specify "The Hand" do
|
71
|
-
# Define some simple specs using a description text and an LTL expression
|
72
|
-
it "isn't always touching the fence" => :"F Hand == somewhere"
|
73
|
-
it "isn't always away form the fence" => :"F Hand == at_fence"
|
74
|
-
end
|
75
|
-
|
76
|
-
# Specification of validity characteristics regarding the pain
|
77
|
-
specify "The pain" do
|
78
|
-
# Use a regular LTL Formula as the expression
|
79
|
-
it "is felt at some point" => :"F Pain == Yes"
|
80
|
-
# Use a more declarative syntax. This becomes useful for complex expressions
|
81
|
-
# as LTL patterns can be used very easily
|
82
|
-
it "is always felt at some point" => ltl.globally.exists(:"Pain == Yes")
|
83
|
-
|
84
|
-
# Pattern: 'Universality', range: 'after q'
|
85
|
-
it "is felt after the switch is activated" => ltl.after(:"PowerSwitch == on").exists(:"Pain == Yes")
|
86
|
-
# Pattern: 'Absence', range: 'before q'
|
87
|
-
it "is never felt before the switch is activated" => ltl.before(:"PowerSwitch == on").never(:"Pain == Yes")
|
88
|
-
# Pattern: 'Reaction', range: 'global'
|
89
|
-
it "always reacts to the switch being activated" => ltl.globally.reacts(:"PowerSwitch == on", :"Pain == Yes")
|
90
|
-
|
91
|
-
# Define an assumption. That assumption must be true for all contained specs
|
92
|
-
assuming "the switch is never activated" => :"G PowerSwitch == off" do
|
93
|
-
it "is never felt " => :"G Pain == No"
|
94
|
-
end
|
95
|
-
|
96
|
-
# Assumptions can be nested and used with the declarative syntax.
|
97
|
-
assuming "the switch is activated" => ltl.globally.exists(:"PowerSwitch == No") do
|
98
|
-
assuming "the hand never touches the fence" => :"G Hand != at_fence" do
|
99
|
-
it "is never felt " => :"G Pain == No"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
File without changes
|