pg-verify 0.1.0

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 (114) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +98 -0
  5. data/README.md +29 -0
  6. data/Rakefile +62 -0
  7. data/bin/console +15 -0
  8. data/bin/pg-verify.rb +18 -0
  9. data/bin/setup +8 -0
  10. data/calc.ebnf +21 -0
  11. data/data/config/pg-verify.yml +66 -0
  12. data/data/nusmv.sample.smv +179 -0
  13. data/data/project-template/.gitignore.resource +4 -0
  14. data/data/project-template/.pg-verify.yml +0 -0
  15. data/data/project-template/README.md +18 -0
  16. data/data/project-template/addon/.keep +0 -0
  17. data/data/project-template/program-graph.rb.resource +103 -0
  18. data/devpg +5 -0
  19. data/doc/examples/railroad_crossing.rb +61 -0
  20. data/doc/examples/train-tree.rb +43 -0
  21. data/doc/examples/weidezaun.rb +99 -0
  22. data/doc/examples/weidezaun.txt +29 -0
  23. data/doc/expose/definition.png +0 -0
  24. data/doc/expose/diagram.png +0 -0
  25. data/doc/expose/expose.md +359 -0
  26. data/doc/expose/validity.png +0 -0
  27. data/exe/pg-verify +4 -0
  28. data/integration_tests/ruby_dsl/001_states.rb +10 -0
  29. data/integration_tests/ruby_dsl/002_transitions.rb +10 -0
  30. data/integration_tests/ruby_dsl/003_actions.rb +14 -0
  31. data/integration_tests/ruby_dsl/004_guards.rb +18 -0
  32. data/integration_tests/ruby_dsl/005_variables.rb +16 -0
  33. data/integration_tests/ruby_dsl/006_state_variables.rb +26 -0
  34. data/integration_tests/ruby_dsl/007_variable_initialization.rb +28 -0
  35. data/integration_tests/ruby_dsl/008_state_initialization.rb +19 -0
  36. data/integration_tests/ruby_dsl/009_shared_variables.rb +26 -0
  37. data/integration_tests/ruby_dsl/010_complex_guards.rb +18 -0
  38. data/integration_tests/ruby_dsl/011_complex_actions.rb +16 -0
  39. data/integration_tests/ruby_dsl/012_error_components.rb +9 -0
  40. data/integration_tests/ruby_dsl/013_hazards.rb +25 -0
  41. data/integration_tests/ruby_dsl/014_tau_transitions.rb +26 -0
  42. data/integration_tests/ruby_dsl/015_basic_dcca.rb +19 -0
  43. data/integration_tests/ruby_dsl/016_pressure_tank.rb +146 -0
  44. data/lib/pg-verify/cli/cli.rb +235 -0
  45. data/lib/pg-verify/core/cmd_runner.rb +151 -0
  46. data/lib/pg-verify/core/core.rb +38 -0
  47. data/lib/pg-verify/core/extensions/array_extensions.rb +11 -0
  48. data/lib/pg-verify/core/extensions/enumerable_extensions.rb +19 -0
  49. data/lib/pg-verify/core/extensions/nil_extensions.rb +7 -0
  50. data/lib/pg-verify/core/extensions/string_extensions.rb +84 -0
  51. data/lib/pg-verify/core/shell/colorizer.rb +136 -0
  52. data/lib/pg-verify/core/shell/shell.rb +0 -0
  53. data/lib/pg-verify/core/util.rb +146 -0
  54. data/lib/pg-verify/doctor/doctor.rb +180 -0
  55. data/lib/pg-verify/ebnf_parser/ast.rb +31 -0
  56. data/lib/pg-verify/ebnf_parser/ebnf_parser.rb +26 -0
  57. data/lib/pg-verify/ebnf_parser/expression_parser.rb +177 -0
  58. data/lib/pg-verify/ebnf_parser/expression_parser2.rb +422 -0
  59. data/lib/pg-verify/ebnf_parser/expressions.ebnf +33 -0
  60. data/lib/pg-verify/ebnf_parser/expressions.peg +52 -0
  61. data/lib/pg-verify/ebnf_parser/parser_result.rb +26 -0
  62. data/lib/pg-verify/interpret/component_context.rb +125 -0
  63. data/lib/pg-verify/interpret/graph_context.rb +85 -0
  64. data/lib/pg-verify/interpret/interpret.rb +142 -0
  65. data/lib/pg-verify/interpret/pg_script.rb +72 -0
  66. data/lib/pg-verify/interpret/spec/ltl_builder.rb +90 -0
  67. data/lib/pg-verify/interpret/spec/spec_context.rb +32 -0
  68. data/lib/pg-verify/interpret/spec/spec_set_context.rb +67 -0
  69. data/lib/pg-verify/interpret/transition_context.rb +55 -0
  70. data/lib/pg-verify/model/allocation_set.rb +28 -0
  71. data/lib/pg-verify/model/assignment.rb +34 -0
  72. data/lib/pg-verify/model/component.rb +40 -0
  73. data/lib/pg-verify/model/dcca/hazard.rb +16 -0
  74. data/lib/pg-verify/model/dcca.rb +67 -0
  75. data/lib/pg-verify/model/expression.rb +106 -0
  76. data/lib/pg-verify/model/graph.rb +58 -0
  77. data/lib/pg-verify/model/model.rb +10 -0
  78. data/lib/pg-verify/model/parsed_expression.rb +77 -0
  79. data/lib/pg-verify/model/simulation/trace.rb +43 -0
  80. data/lib/pg-verify/model/simulation/variable_state.rb +23 -0
  81. data/lib/pg-verify/model/source_location.rb +45 -0
  82. data/lib/pg-verify/model/specs/spec.rb +44 -0
  83. data/lib/pg-verify/model/specs/spec_result.rb +25 -0
  84. data/lib/pg-verify/model/specs/spec_set.rb +43 -0
  85. data/lib/pg-verify/model/specs/specification.rb +50 -0
  86. data/lib/pg-verify/model/transition.rb +41 -0
  87. data/lib/pg-verify/model/validation/assignment_to_state_variable_validation.rb +26 -0
  88. data/lib/pg-verify/model/validation/empty_state_set_validation.rb +18 -0
  89. data/lib/pg-verify/model/validation/errors.rb +119 -0
  90. data/lib/pg-verify/model/validation/foreign_assignment_validation.rb +30 -0
  91. data/lib/pg-verify/model/validation/unknown_token_validation.rb +35 -0
  92. data/lib/pg-verify/model/validation/validation.rb +23 -0
  93. data/lib/pg-verify/model/variable.rb +47 -0
  94. data/lib/pg-verify/model/variable_set.rb +84 -0
  95. data/lib/pg-verify/nusmv/nusmv.rb +23 -0
  96. data/lib/pg-verify/nusmv/runner.rb +124 -0
  97. data/lib/pg-verify/puml/puml.rb +23 -0
  98. data/lib/pg-verify/shell/loading/line_animation.rb +36 -0
  99. data/lib/pg-verify/shell/loading/loading_animation.rb +80 -0
  100. data/lib/pg-verify/shell/loading/loading_prompt.rb +43 -0
  101. data/lib/pg-verify/shell/loading/no_animation.rb +20 -0
  102. data/lib/pg-verify/shell/shell.rb +30 -0
  103. data/lib/pg-verify/simulation/simulation.rb +7 -0
  104. data/lib/pg-verify/simulation/simulator.rb +90 -0
  105. data/lib/pg-verify/simulation/state.rb +53 -0
  106. data/lib/pg-verify/transform/hash_transformation.rb +104 -0
  107. data/lib/pg-verify/transform/nusmv_transformation.rb +261 -0
  108. data/lib/pg-verify/transform/puml_transformation.rb +89 -0
  109. data/lib/pg-verify/transform/transform.rb +8 -0
  110. data/lib/pg-verify/version.rb +5 -0
  111. data/lib/pg-verify.rb +47 -0
  112. data/pg-verify.gemspec +38 -0
  113. data/sig/pg-verify.rbs +4 -0
  114. metadata +226 -0
@@ -0,0 +1,180 @@
1
+ module PgVerify
2
+ module Doctor
3
+
4
+ Warning = Struct.new(:title, :text)
5
+
6
+ class DoctorError < PgVerify::Core::Error
7
+ def initialize(warnings)
8
+ @warnings = warnings
9
+ end
10
+ def formatted()
11
+ is_are, s = @warnings.length == 1 ? ["is", ""] : ["are", "s"]
12
+ header = "There #{is_are} #{@warnings.length} warning#{s} for your installation:"
13
+ string = @warnings.each_with_index.map { |w, i|
14
+ title = "#{i + 1}) #{w.title}".c_warn
15
+ body = w.text.indented(str: " ")
16
+ "#{title}\n#{body}"
17
+ }.join("\n\n")
18
+ return header, string
19
+ end
20
+ end
21
+
22
+ def self.check()
23
+ checks = Doctor.methods
24
+ .select { |method| method.to_s.start_with?("check_") }
25
+ .map { |sym| sym.to_s.sub("check_", "").to_sym }
26
+ .sort
27
+ warnings = checks.map { |check| run_check(check) }.flatten.compact
28
+ raise DoctorError.new(warnings) unless warnings.empty?
29
+ end
30
+
31
+ def self.run_check(symbol)
32
+ Shell::LoadingPrompt.while_loading("Checking #{symbol.to_s.gsub('_', ' ').c_string}") {
33
+ warnings = ([self.send(:"check_#{symbol}")] || []).flatten.compact
34
+ state = warnings.empty? ? :success : :error
35
+ msg = warnings.empty? ? "Ok" : "#{warnings.length} warning(s)!"
36
+ # Allow returning :skip from the check to mark the check as skipped
37
+ if warnings.length == 1 && warnings.first == :skip
38
+ state, msg, warnings = :empty, "Skipped!", []
39
+ end
40
+ Shell::LoadingPrompt::LoadingResult.new(warnings, msg, state: state)
41
+ }
42
+ end
43
+
44
+ def self.check_01_Can_find_NuSMV()
45
+ return [] unless PgVerify::NuSMV::Runner.new.find_nusmv_path.nil?
46
+ return Warning.new("Unable to locate the NuSMV executable",
47
+ "Make sure to install NuSMV by unpacking it and placing the entire folder into\n" \
48
+ "the #{'addon'.c_file} directory of your project. " \
49
+ "(#{File.expand_path('addon').c_sidenote})\n" \
50
+ "Alternatively you can set the #{'nusmv.path'.c_string} in the configuration."
51
+ )
52
+ end
53
+
54
+ def self.check_02_Can_run_NuSMV()
55
+ path = PgVerify::NuSMV::Runner.new.find_nusmv_path
56
+ return :skip if path.nil?
57
+
58
+ # Test by evaluating some example smv file
59
+ example_file = File.join(PgVerify.root, "data", "nusmv.sample.smv")
60
+ return [] if Core::CMDRunner.run_for_exit_code("#{path} #{example_file}") == 0
61
+
62
+ return Warning.new("Unable to run the NuSMV executable",
63
+ "NuSMV could be found here: #{path.c_file}\n" \
64
+ "However it could not be executed. Here are a few things to try:\n" \
65
+ " - Make sure the file is executable\n" \
66
+ " - Make sure you have the required permissions"
67
+ )
68
+ end
69
+
70
+ def self.check_03_Run_integration_tests()
71
+ return :skip if PgVerify::NuSMV::Runner.new.find_nusmv_path.nil?
72
+
73
+ warnings = []
74
+
75
+ test_files = Dir[File.join(PgVerify.root, "integration_tests", "ruby_dsl", "*.rb")].sort
76
+ warnings += test_files.map { |test_file|
77
+ model = Interpret::PgScript.new.interpret(test_file).first
78
+ PgVerify::Model::Validation.validate!(model)
79
+ results = NuSMV::Runner.new().run_specs(model)
80
+ failures = results.reject(&:success)
81
+ next if failures.empty?
82
+
83
+ test_name = File.basename(test_file, '.*').gsub("_", "-")
84
+ failures_s = failures.map { |f| "#{f.spec.text} (#{f.spec.expression.to_s.c_blue})" }
85
+ show_command = "$ pg-verify show nusmv --script #{File.expand_path(test_file)}".c_cyan
86
+ test_command = "$ pg-verify test --script #{File.expand_path(test_file)}".c_cyan
87
+ Warning.new("Failed integration test in #{test_name}",
88
+ "The test #{test_name.c_string} contains the following unsatisfied specifications:\n" \
89
+ "\t- #{failures_s.join("\n\t- ")}\n" \
90
+ "These specifications should be valid if pg-verify works as expected.\n" \
91
+ "You can use the following commands to debug this:\n" \
92
+ " #{show_command}\n #{test_command}"
93
+ )
94
+ }.compact
95
+
96
+ warnings += test_files.map { |test_file|
97
+ model = Interpret::PgScript.new.interpret(test_file).first
98
+ next if model.hazards.empty?
99
+ require 'set'
100
+
101
+ dcca = Model::DCCA.new(model, NuSMV::Runner.new)
102
+ result = dcca.perform()
103
+
104
+ expected_cut_sets = File.read(test_file)
105
+ .split("\n")
106
+ .find { |l| l.include?("Expected Cut Sets") }
107
+ .scan(/\{([^}]+)\}/).flatten
108
+ .map { |string| string.split(",").map(&:strip).map(&:to_sym) }
109
+ .map { |cut_set| Set.new(cut_set) }
110
+
111
+ actual_cut_sets = result.values.first.map { |cut_set| Set.new(cut_set) }
112
+
113
+ next if Set.new(actual_cut_sets) == Set.new(expected_cut_sets)
114
+
115
+ Warning.new("Failed DCCA for #{File.basename(test_file)}",
116
+ "The DCCA for hazard #{result.keys.first.expression.to_s.c_blue} yielded unexpected cut sets:\n" \
117
+ "Expected: #{expected_cut_sets}\n" \
118
+ "Got : #{actual_cut_sets}\n"
119
+ )
120
+ }.compact
121
+
122
+
123
+ return warnings
124
+ end
125
+
126
+ def self.check_04_Check_project_files()
127
+ warnings = []
128
+ script = Settings.ruby_dsl.default_script_name
129
+
130
+ return Warning.new("Failed to find model definition at #{script}",
131
+ "If you run PG verify without any arguments it expects a program graph definition\n" \
132
+ "at your working directory, which should be named #{script.c_file} by default.\n" \
133
+ "Based on your current working directory this would be this full path:\n" \
134
+ "\t#{File.expand_path(script).c_file}"
135
+ ) unless File.file?(script)
136
+
137
+ begin
138
+ Interpret::PgScript.new.interpret(script)
139
+ rescue Exception => e
140
+ return Warning.new("Failed to interpret model at #{script}",
141
+ "Your default model definition at #{script.c_file}\n" \
142
+ "(Full path: #{File.expand_path(script).c_sidenote})\n" \
143
+ "Could not be interpreted. Make sure there are no syntax errors."
144
+ )
145
+ end
146
+
147
+ return []
148
+ end
149
+
150
+ # def self.check_03_Can_find_PlantUML()
151
+ # return [] unless PgVerify::Puml.find_path.nil?
152
+ # return Warning.new("Unable to find the PlantUML executable",
153
+ # "Make sure to install PlantUML by dropping the jar into\n" \
154
+ # "the #{'addon'.c_file} directory of your project. " \
155
+ # "(#{File.expand_path('addon').c_sidenote})\n" \
156
+ # "You can get it from here: #{"https://plantuml.com/download".c_string}\n" \
157
+ # "pg-verify will fall back to using the PlantUML web-server otherwise."
158
+ # )
159
+ # end
160
+
161
+ # def self.check_04_Can_run_PlantUML()
162
+ # path = PgVerify::Puml.find_path
163
+ # return :skip if path.blank?
164
+
165
+ # return [] if Core::CMDRunner.run_for_exit_code("java -jar #{path} -help") == 0
166
+ # puts "java -jar #{path} -help"
167
+
168
+ # return Warning.new("Unable to run the PlantUML executable",
169
+ # "PlantUML could be found here: #{path.c_file}\n" \
170
+ # "However it could not be executed. Here are a few things to try:\n" \
171
+ # " - Make sure the file is executable\n" \
172
+ # " - Make sure you have the required permissions"
173
+ # )
174
+ # end
175
+
176
+ end
177
+ end
178
+
179
+ # Require all module files
180
+ Dir[File.join(__dir__, "**", '*.rb')].sort.each { |file| require file }
@@ -0,0 +1,31 @@
1
+ module PgVerify
2
+ module EbnfParser
3
+
4
+ class Ast
5
+
6
+ attr_accessor :map
7
+
8
+ def initialize(map)
9
+ @map = map
10
+ end
11
+
12
+ def find_variables(element=@map)
13
+ # TODO: Actually parse the variables
14
+ # puts "REC #{element.class}: #{element}"
15
+ # puts "\n"
16
+ # ret = []
17
+ # ret += element.map { |val| find_variables(val) }.flatten if element.is_a?(Array)
18
+ # element.each { |key, value|
19
+ # # We found a variable if we found a value which is not a number nor a boolean constant
20
+ # if key == :Value && value.is_a?(String) && !(/\d+/.match?(value)) && value != 'true' && value != 'false'
21
+ # ret << value
22
+ # end
23
+ # ret += find_variables(value)
24
+ # } if element.is_a?(Hash)
25
+ # return ret
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ require 'ebnf'
2
+ require 'ebnf/terminals'
3
+ require 'ebnf/peg/parser'
4
+ require 'sxp'
5
+ require 'logger'
6
+ require 'json'
7
+
8
+ # Require all module files
9
+ Dir[File.join(__dir__, "**", '*.rb')].sort.each { |file| require file }
10
+
11
+ module PgVerify
12
+ module EbnfParser
13
+
14
+ def self.parse_expression(expression, type: :Expression)
15
+ parser = ExpressionParser.new(type: type)
16
+ error, ast = nil, nil
17
+ begin
18
+ ast = parser.parse!(expression)
19
+ rescue EBNF::PEG::Parser::Error => e
20
+ error = e
21
+ end
22
+ return ParserResult.new(ast, error)
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,177 @@
1
+ module PgVerify
2
+ module EbnfParser
3
+
4
+ class ExpressionParser
5
+ include EBNF::PEG::Parser
6
+
7
+ # Abstract syntax tree from parse
8
+ attr_reader :ast
9
+ attr_accessor :rules
10
+ attr_accessor :options
11
+ attr_accessor :type
12
+
13
+
14
+ # production(:Assignment, clear_packrat: true) do |value|
15
+ # # puts"Assignment:".ljust(25) + " #{value}"
16
+ # value
17
+ # end
18
+
19
+ # production(:BoolExpr, clear_packrat: true) do |value|
20
+ # # puts"BoolExpr:".ljust(25) + " #{value}"
21
+ # value
22
+ # end
23
+
24
+ # production(:Equivalence, clear_packrat: true) do |value|
25
+ # # puts"Equivalence:".ljust(25) + " #{value}"
26
+ # value
27
+ # end
28
+
29
+ # production(:Implication, clear_packrat: true) do |value|
30
+ # # puts"Implication:".ljust(25) + " #{value}"
31
+ # value
32
+ # end
33
+
34
+ # production(:Disjunction, clear_packrat: true) do |value|
35
+ # # puts"Disjunction:".ljust(25) + " #{value}"
36
+ # value
37
+ # end
38
+
39
+ # production(:Konjunction, clear_packrat: true) do |value|
40
+ # # puts"Konjunction:".ljust(25) + " #{value}"
41
+ # value
42
+ # end
43
+
44
+ # production(:Negation, clear_packrat: true) do |value|
45
+ # puts"Negation:".ljust(25) + "#{value.class} #{value}"
46
+ # value
47
+ # end
48
+
49
+ # production(:Comparison, clear_packrat: true) do |value|
50
+ # # puts"Comparison:".ljust(25) + " #{value}"
51
+ # value
52
+ # end
53
+
54
+ # production(:IntExpr, clear_packrat: true) do |value|
55
+ # # puts"IntExpr:".ljust(25) + " #{value}"
56
+ # value
57
+ # end
58
+
59
+ # production(:Sum, clear_packrat: true) do |value|
60
+ # # puts"Sum:".ljust(25) + " #{value}"
61
+ # value
62
+ # end
63
+
64
+ # production(:Product, clear_packrat: true) do |value|
65
+ # # puts"Product:".ljust(25) + " #{value}"
66
+ # valuex
67
+ # end
68
+
69
+
70
+
71
+ # [{:Value=>"3"}, {:_Product_1=>"* 5 * 5"}]
72
+ production(:Product) do |value|
73
+ val = value.first[:Value]
74
+ prod1 = value.last[:_Product_1]
75
+ ret = "#{val} #{prod1}"
76
+ puts"Product:".ljust(25) + " #{value} -> #{ret}"
77
+ ret
78
+ end
79
+
80
+ # A list of product operations plus values ['+ 4', '+ 4']
81
+ production(:_Product_1) do |value|
82
+ ret = value.join(' ')
83
+ # puts"Product1:".ljust(25) + " #{value} -> #{ret}"
84
+ ret
85
+ end
86
+
87
+ # A product operation plus value '+ 4'
88
+ production(:_Product_2) do |value|
89
+ op = value.first[:_Product_3]
90
+ val = value.last[:Value]
91
+ ret = "#{op} #{val}"
92
+ puts "Product2:".ljust(25) + " #{value} -> #{ret}"
93
+ ret
94
+ end
95
+
96
+ # A product operation '*' '/'
97
+ production(:_Product_3) do |value|
98
+ operation = value
99
+ value
100
+ end
101
+
102
+ # An atomic value like '3', 'true' or 'variable'
103
+ production(:Value) do |value|
104
+ value
105
+ end
106
+ # An expression in braces
107
+ production(:_Value_1) do |value|
108
+ value.is_a?(Hash) ? value[:Expression] : value
109
+ end
110
+ # A number like '42' or '-7'
111
+ production(:NUMBER) do |value|
112
+ value
113
+ end
114
+ # A boolean value like 'true' or 'false'
115
+ production(:BOOL) do |value|
116
+ value
117
+ end
118
+ # A boolean expression in braces like '(3 < 2 <-> true)'
119
+ production(:_BOOL_1) do |value|
120
+ value.is_a?(Hash) ? value[:BoolExpr] : value
121
+ value
122
+ end
123
+ # Integer comparison operators like '>' and '!='
124
+ production(:CMPOP) do |value|
125
+ value
126
+ end
127
+
128
+
129
+ def initialize(type: :Expression)
130
+ @type = type
131
+ @options = {}
132
+ # @options[:logger] = Logger.new(STDERR)
133
+ # @options[:logger].level = :info
134
+ # @options[:logger].formatter = lambda {|severity, datetime, progname, msg| "#{severity} #{msg}\n"}
135
+
136
+ # Intantiate the grammar
137
+ ebnf_file = File.expand_path("expressions.ebnf", __dir__)
138
+ # Perform PEG-specific transformation to the associated rules, which will be passed directly to the parser.
139
+ @rules = EBNF.parse(File.open(ebnf_file)).make_peg.ast
140
+
141
+ skeletton = EBNF.parse(File.open(ebnf_file)).make_peg.to_s.split("\n").reverse.map { |line|
142
+ rule = line.split(/\s+/)[1]
143
+ comment = "# Rule: '#{line.split(/\s+/).join(" ")}'"
144
+ puts_line = 'puts "' + rule + ': \'#{input}\' -> \'#{output}\'"'
145
+ body = "\toutput = input\n\t#{puts_line}\n\toutput"
146
+ method = "production(:#{rule}) do |input|\n#{body}\nend"
147
+ [comment, method].join("\n")
148
+ }.join("\n\n")
149
+ File.write(File.expand_path("expressions.rb", __dir__), skeletton)
150
+
151
+ # TODO: Remove
152
+ File.write(File.expand_path("expressions.peg", __dir__), EBNF.parse(File.open(ebnf_file)).make_peg)
153
+ end
154
+
155
+ def parse!(input)
156
+ raise "Empty expression!" if input.nil? || input.empty?
157
+ ast_map = parse(input, @type, @rules, **@options)
158
+ Ast.new(ast_map)
159
+ end
160
+
161
+ # def evaluate!(input, type: :Expression)
162
+ # # TODO: Change this to actually eval (Also in specs)
163
+ # parse(input, type, @rules, **@options)
164
+ # end
165
+
166
+ # def accepts?(input, type: :Expression)
167
+ # begin
168
+ # evaluate!(input, type: type)
169
+ # return true
170
+ # rescue EBNF::PEG::Parser::Error => e
171
+ # return false
172
+ # end
173
+ # end
174
+ end
175
+
176
+ end
177
+ end