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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -20
  3. data/README.md +1 -1
  4. data/data/banner.txt +5 -0
  5. data/data/project-template/README.md +81 -0
  6. data/doc/examples/vending_machine/checkpoint_1.rb +29 -0
  7. data/doc/examples/vending_machine/checkpoint_2.rb +47 -0
  8. data/doc/examples/vending_machine/checkpoint_3.rb +68 -0
  9. data/doc/examples/vending_machine/checkpoint_4.rb +202 -0
  10. data/integration_tests/ruby_dsl/001_states.rb +2 -1
  11. data/integration_tests/ruby_dsl/002_transitions.rb +2 -1
  12. data/integration_tests/ruby_dsl/017_ctl_specifications.rb +19 -0
  13. data/integration_tests/ruby_dsl/018_non_atomic_hazard.rb +17 -0
  14. data/integration_tests/ruby_dsl/019_multiple_actions.rb +21 -0
  15. data/integration_tests/ruby_dsl/020_vending_machine.rb +188 -0
  16. data/lib/pg-verify/cli/cli.rb +94 -24
  17. data/lib/pg-verify/cli/cli_utils.rb +61 -0
  18. data/lib/pg-verify/interpret/component_context.rb +1 -1
  19. data/lib/pg-verify/interpret/interpret.rb +1 -1
  20. data/lib/pg-verify/interpret/pg_script.rb +1 -0
  21. data/lib/pg-verify/model/parsed_expression.rb +10 -5
  22. data/lib/pg-verify/model/simulation/trace.rb +15 -4
  23. data/lib/pg-verify/model/validation/errors.rb +21 -2
  24. data/lib/pg-verify/nusmv/runner.rb +69 -17
  25. data/lib/pg-verify/puml/puml.rb +1 -0
  26. data/lib/pg-verify/transform/hash_transformation.rb +46 -14
  27. data/lib/pg-verify/transform/nusmv_transformation.rb +4 -3
  28. data/lib/pg-verify/transform/puml_transformation.rb +27 -8
  29. data/lib/pg-verify/version.rb +1 -1
  30. data/pg-verify.gemspec +0 -1
  31. data/vscript.rb +64 -0
  32. metadata +14 -18
  33. data/data/project-template/program-graph.rb.resource +0 -103
  34. /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
- nusmv_s = Transform::NuSmvTransformation.new.transform_graph(program_graph)
9
- output = eval_nusmv(nusmv_s)
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 << "read_model"
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
- if line.include?("Loop starts here")
80
- loop_index == var_states.length - 1
81
- next
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
- return Model::Trace.new(program_graph, var_states)
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: [])
@@ -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
- return { graph.name.to_s =>
8
- graph.components.map { |c| transform_component(graph, c) }
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
- if range.is_a?(String) && range.match(/\A\d+\.\.\d+\z/)
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&.to_s,
89
- "guard" => transition.guard&.to_s,
90
- "action" => transition.action&.to_s,
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
- expression_s += "LTLSPEC " + transform_expression(spec.expression, varset)
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
- label = [ transition.precon, transition.guard ].map(&:to_s).reject(&:empty?).join(" && ")
63
- label += "/ " + transition.action.to_s unless transition.action.nil?
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 { |v| "#{v.name} => #{transform_range(v.range)}" }.join("\n")
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgVerify
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
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.0
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-02-12 00:00:00.000000000 Z
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