Class: OpenC3::ConfigParser
- Defined in:
- lib/openc3/config/config_parser.rb,
ext/openc3/ext/config_parser/config_parser.c
Overview
Reads OpenC3 style configuration data which consists of keywords followed by 0 or more comma delimited parameters. Parameters with spaces must be enclosed in quotes. Quotes should also be used to indicate a parameter is a string. Keywords are case-insensitive and will be returned in uppercase.
Defined Under Namespace
Classes: Error
Constant Summary collapse
- PARSING_REGEX =
Regular expression used to break up an individual line into a keyword and comma delimited parameters. Handles parameters in single or double quotes.
%r{ (?:"(?:[^\\"]|\\.)*") | (?:'(?:[^\\']|\\.)*') | \S+ }x
- @@message_callback =
nil
- @@progress_callback =
nil
- @@splash =
Holds the current splash screen
nil
Instance Attribute Summary collapse
-
#filename ⇒ String
The name of the configuration file being parsed.
-
#keyword ⇒ String
The current keyword being parsed.
-
#line ⇒ String
The current line being parsed.
-
#line_number ⇒ Integer
The current line number being parsed.
-
#parameters ⇒ Array<String>
The parameters found after the keyword.
-
#url ⇒ String
The default URL to use in errors.
Class Method Summary collapse
-
.handle_defined_constants(value, data_type = nil, bit_size = nil) ⇒ Numeric
Converts a string representing a defined constant into its value.
-
.handle_nil(value) ⇒ nil|Object
Converts a String containing ”, ‘NIL’, ‘NULL’, or ‘NONE’ to nil Ruby primitive.
-
.handle_true_false(value) ⇒ true|false|Object
Converts a String containing ‘TRUE’ or ‘FALSE’ to true or false Ruby primitive.
-
.handle_true_false_nil(value) ⇒ true|false|nil|Object
Converts a String containing ”, ‘NIL’, ‘NULL’, ‘TRUE’ or ‘FALSE’ to nil, true or false Ruby primitives.
- .message_callback=(message_callback) ⇒ Object
- .progress_callback=(progress_callback) ⇒ Object
-
.splash ⇒ Object
Returns the current splash screen if present.
- .splash=(splash) ⇒ Object
Instance Method Summary collapse
-
#error(message, usage = "", url = @url) ⇒ Error
Creates an Error.
-
#initialize(url = "https://docs.openc3.com/docs") ⇒ ConfigParser
constructor
A new instance of ConfigParser.
-
#parse_file(filename, yield_non_keyword_lines = false, remove_quotes = true, run_erb = true, variables = {}) {|keyword, parameters| ... } ⇒ Object
Processes a file and yields |config| to the given block.
-
#parse_loop(io, yield_non_keyword_lines, remove_quotes, size, rx) ⇒ Object
Iterates over each line of the io object and yields the keyword and parameters.
-
#read_file(filename) ⇒ Object
Can be called during parsing to read a referenced file.
-
#render(template_name, options = {}) ⇒ Object
Called by the ERB template to render a partial.
-
#verify_num_parameters(min_num_params, max_num_params, usage = "") ⇒ Object
Verifies the parameters in the config parameter have the specified number of parameter and raises an Error if not.
-
#verify_parameter_naming(index, usage = "") ⇒ Object
Verifies the indicated parameter in the config doesn’t start or end with an underscore, doesn’t contain a double underscore or double bracket, doesn’t contain spaces and doesn’t start with a close bracket.
Constructor Details
#initialize(url = "https://docs.openc3.com/docs") ⇒ ConfigParser
Returns a new instance of ConfigParser.
146 147 148 |
# File 'lib/openc3/config/config_parser.rb', line 146 def initialize(url = "https://docs.openc3.com/docs") @url = url end |
Instance Attribute Details
#filename ⇒ String
Returns The name of the configuration file being parsed. This will be an empty string if the parse_string class method is used.
42 43 44 |
# File 'lib/openc3/config/config_parser.rb', line 42 def filename @filename end |
#keyword ⇒ String
Returns The current keyword being parsed.
35 36 37 |
# File 'lib/openc3/config/config_parser.rb', line 35 def keyword @keyword end |
#line ⇒ String
Returns The current line being parsed. This is the raw string which is useful when printing errors.
46 47 48 |
# File 'lib/openc3/config/config_parser.rb', line 46 def line @line end |
#line_number ⇒ Integer
Returns The current line number being parsed. This will still be populated when using parse_string because lines still must be delimited by newline characters.
51 52 53 |
# File 'lib/openc3/config/config_parser.rb', line 51 def line_number @line_number end |
#parameters ⇒ Array<String>
Returns The parameters found after the keyword.
38 39 40 |
# File 'lib/openc3/config/config_parser.rb', line 38 def parameters @parameters end |
#url ⇒ String
Returns The default URL to use in errors. The URL can still be overridden by directly passing it to the error method.
55 56 57 |
# File 'lib/openc3/config/config_parser.rb', line 55 def url @url end |
Class Method Details
.handle_defined_constants(value, data_type = nil, bit_size = nil) ⇒ Numeric
Converts a string representing a defined constant into its value. The defined constants are the minimum and maximum values for all the allowable data types. [MIN/MAX]_[U]INT and [MIN/MAX]_FLOAT. Thus MIN_UINT8, MAX_INT32, and MIN_FLOAT64 are all allowable values. Any other strings raise ArgumentError but all other types are simply returned.
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/openc3/config/config_parser.rb', line 333 def self.handle_defined_constants(value, data_type = nil, bit_size = nil) if value.class == String case value.upcase when 'MIN', 'MAX' return self.calculate_range_value(value.upcase, data_type, bit_size) when 'MIN_INT8' return -128 when 'MAX_INT8' return 127 when 'MIN_INT16' return -32768 when 'MAX_INT16' return 32767 when 'MIN_INT32' return -2147483648 when 'MAX_INT32' return 2147483647 when 'MIN_INT64' return -9223372036854775808 when 'MAX_INT64' return 9223372036854775807 when 'MIN_UINT8', 'MIN_UINT16', 'MIN_UINT32', 'MIN_UINT64' return 0 when 'MAX_UINT8' return 255 when 'MAX_UINT16' return 65535 when 'MAX_UINT32' return 4294967295 when 'MAX_UINT64' return 18446744073709551615 when 'MIN_FLOAT64' return -Float::MAX when 'MAX_FLOAT64' return Float::MAX when 'MIN_FLOAT32' return -3.402823e38 when 'MAX_FLOAT32' return 3.402823e38 when 'POS_INFINITY' return Float::INFINITY when 'NEG_INFINITY' return -Float::INFINITY else raise ArgumentError, "Could not convert constant: #{value}" end end return value end |
.handle_nil(value) ⇒ nil|Object
Converts a String containing ”, ‘NIL’, ‘NULL’, or ‘NONE’ to nil Ruby primitive. All other arguments are simply returned.
278 279 280 281 282 283 284 285 286 |
# File 'lib/openc3/config/config_parser.rb', line 278 def self.handle_nil(value) if String === value case value.upcase when '', 'NIL', 'NULL', 'NONE' return nil end end return value end |
.handle_true_false(value) ⇒ true|false|Object
Converts a String containing ‘TRUE’ or ‘FALSE’ to true or false Ruby primitive. All other values are simply returned.
293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/openc3/config/config_parser.rb', line 293 def self.handle_true_false(value) if String === value case value.upcase when 'TRUE' return true when 'FALSE' return false end end return value end |
.handle_true_false_nil(value) ⇒ true|false|nil|Object
Converts a String containing ”, ‘NIL’, ‘NULL’, ‘TRUE’ or ‘FALSE’ to nil, true or false Ruby primitives. All other values are simply returned.
310 311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/openc3/config/config_parser.rb', line 310 def self.handle_true_false_nil(value) if String === value case value.upcase when 'TRUE' return true when 'FALSE' return false when '', 'NIL', 'NULL' return nil end end return value end |
.message_callback=(message_callback) ⇒ Object
62 63 64 |
# File 'lib/openc3/config/config_parser.rb', line 62 def self.() @@message_callback = end |
.progress_callback=(progress_callback) ⇒ Object
72 73 74 |
# File 'lib/openc3/config/config_parser.rb', line 72 def self.progress_callback=(progress_callback) @@progress_callback = progress_callback end |
.splash ⇒ Object
Returns the current splash screen if present
94 95 96 |
# File 'lib/openc3/config/config_parser.rb', line 94 def self.splash @@splash end |
.splash=(splash) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/openc3/config/config_parser.rb', line 81 def self.splash=(splash) if splash @@splash = splash @@progress_callback = splash.progress_callback @@message_callback = splash. else @@splash = nil @@progress_callback = nil @@message_callback = nil end end |
Instance Method Details
#error(message, usage = "", url = @url) ⇒ Error
Creates an Error
156 157 158 |
# File 'lib/openc3/config/config_parser.rb', line 156 def error(, usage = "", url = @url) return Error.new(self, , usage, url) end |
#parse_file(filename, yield_non_keyword_lines = false, remove_quotes = true, run_erb = true, variables = {}) {|keyword, parameters| ... } ⇒ Object
Processes a file and yields |config| to the given block
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/openc3/config/config_parser.rb', line 197 def parse_file(filename, yield_non_keyword_lines = false, remove_quotes = true, run_erb = true, variables = {}, &) raise Error.new(self, "Configuration file #{filename} does not exist.") unless filename && File.exist?(filename) @filename = filename # Create a temp file where we write the ERB parsed output file = create_parsed_output_file(filename, run_erb, variables) size = file.stat.size.to_f # Callbacks for beginning of parsing @@message_callback.call("Parsing #{size} bytes of #{filename}") if @@message_callback @@progress_callback.call(0.0) if @@progress_callback begin # Loop through each line of the data parse_loop(file, yield_non_keyword_lines, remove_quotes, size, PARSING_REGEX, &) ensure file.close unless file.closed? end end |
#parse_loop(io, yield_non_keyword_lines, remove_quotes, size, rx) ⇒ Object
Iterates over each line of the io object and yields the keyword and parameters
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
# File 'lib/openc3/config/config_parser.rb', line 477 def parse_loop(io, yield_non_keyword_lines, remove_quotes, size, rx) string_concat = false @line_number = 0 @keyword = nil @parameters = [] @line = '' errors = [] while true @line_number += 1 if @@progress_callback && ((@line_number % 10) == 0) @@progress_callback.call(io.pos / size) if size > 0.0 end begin line = io.readline rescue Exception break end line.strip! # Ensure the line length is not 0 next if line.length == 0 if string_concat # Skip comment lines after a string concatenation if (line[0] == '#') next end # Remove the opening quote if we're continuing the line line = line[1..-1] end # Check for string continuation case line[-1] when '+', '\\' # String concatenation newline = line[-1] == '+' # Trim off the concat character plus any spaces, e.g. "line" \ trim = line[0..-2].strip() # Now trim off the last quote so it will flow into the next line @line += trim[0..-2] @line += "\n" if newline string_concat = true next when '&' # Line continuation @line += line[0..-2] next else @line += line end string_concat = false data = @line.scan(rx) first_item = '' if data.length > 0 first_item += data[0] end if (first_item.length == 0) || (first_item[0] == '#') @keyword = nil else @keyword = first_item.upcase end @parameters = [] # Ignore lines without keywords: comments and blank lines if @keyword.nil? if yield_non_keyword_lines begin yield(@keyword, @parameters) rescue => e errors << e end end @line = '' next end length = data.length if length > 1 (1..(length - 1)).each do |index| string = data[index] # Don't process trailing comments such as: # KEYWORD PARAM #This is a comment # But still process Ruby string interpolations such as: # KEYWORD PARAM #{var} if (string.length > 0) && (string[0] == '#') if !((string.length > 1) && (string[1] == '{')) break end end if remove_quotes @parameters << string.remove_quotes else @parameters << string end end end begin yield(@keyword, @parameters) rescue => e errors << e end @line = '' end parse_errors(errors) @@progress_callback.call(1.0) if @@progress_callback return nil end |
#read_file(filename) ⇒ Object
Can be called during parsing to read a referenced file
173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/openc3/config/config_parser.rb', line 173 def read_file(filename) # Assume the file is there. If not we raise a pretty obvious error if File.(filename) == filename # absolute path path = filename else # relative to the current @filename path = File.join(File.dirname(@filename), filename) end OpenC3.set_working_dir(File.dirname(path)) do return File.read(path).force_encoding("UTF-8") end end |
#render(template_name, options = {}) ⇒ Object
Called by the ERB template to render a partial
161 162 163 164 165 166 167 168 169 170 |
# File 'lib/openc3/config/config_parser.rb', line 161 def render(template_name, = {}) raise Error.new(self, "Partial name '#{template_name}' must begin with an underscore.") if File.basename(template_name)[0] != '_' b = binding if [:locals] [:locals].each { |key, value| b.local_variable_set(key, value) } end return ERB.new(read_file(template_name).comment_erb(), trim_mode: "-").result(b) end |
#verify_num_parameters(min_num_params, max_num_params, usage = "") ⇒ Object
Verifies the parameters in the config parameter have the specified number of parameter and raises an Error if not.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/openc3/config/config_parser.rb', line 234 def verify_num_parameters(min_num_params, max_num_params, usage = "") # This syntax works with 0 because each doesn't return any values # for a backwards range (1..min_num_params).each do |index| # If the parameter is nil (0 based) then we have a problem if @parameters[index - 1].nil? raise Error.new(self, "Not enough parameters for #{@keyword}.", usage, @url) end end # If they pass nil for max_params we don't check for a maximum number if max_num_params && !@parameters[max_num_params].nil? raise Error.new(self, "Too many parameters for #{@keyword}.", usage, @url) end end |
#verify_parameter_naming(index, usage = "") ⇒ Object
Verifies the indicated parameter in the config doesn’t start or end with an underscore, doesn’t contain a double underscore or double bracket, doesn’t contain spaces and doesn’t start with a close bracket.
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/openc3/config/config_parser.rb', line 254 def verify_parameter_naming(index, usage = "") param = @parameters[index - 1] if param.end_with? '_' raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot end with an underscore ('_').", usage, @url) end if param.include? '__' raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain a double underscore ('__').", usage, @url) end if param.include? '[[' or param.include? ']]' raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain double brackets ('[[' or ']]').", usage, @url) end if param.include? ' ' raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot contain a space (' ').", usage, @url) end if param.start_with?('}') raise Error.new(self, "Parameter #{index} (#{param}) for #{@keyword} cannot start with a close bracket ('}').", usage, @url) end end |