pg-verify 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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