diff options
Diffstat (limited to 'lib/puppet/external/pson/pure/parser.rb')
-rw-r--r-- | lib/puppet/external/pson/pure/parser.rb | 486 |
1 files changed, 243 insertions, 243 deletions
diff --git a/lib/puppet/external/pson/pure/parser.rb b/lib/puppet/external/pson/pure/parser.rb index 2d0a03066..6048f67e0 100644 --- a/lib/puppet/external/pson/pure/parser.rb +++ b/lib/puppet/external/pson/pure/parser.rb @@ -1,264 +1,264 @@ require 'strscan' module PSON - module Pure - # This class implements the PSON parser that is used to parse a PSON string - # into a Ruby data structure. - class Parser < StringScanner - STRING = /" ((?:[^\x0-\x1f"\\] | - # escaped special characters: - \\["\\\/bfnrt] | - \\u[0-9a-fA-F]{4} | - # match all but escaped special characters: - \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*) - "/nx - INTEGER = /(-?0|-?[1-9]\d*)/ - FLOAT = /(-? - (?:0|[1-9]\d*) - (?: - \.\d+(?i:e[+-]?\d+) | - \.\d+ | - (?i:e[+-]?\d+) - ) - )/x - NAN = /NaN/ - INFINITY = /Infinity/ - MINUS_INFINITY = /-Infinity/ - OBJECT_OPEN = /\{/ - OBJECT_CLOSE = /\}/ - ARRAY_OPEN = /\[/ - ARRAY_CLOSE = /\]/ - PAIR_DELIMITER = /:/ - COLLECTION_DELIMITER = /,/ - TRUE = /true/ - FALSE = /false/ - NULL = /null/ - IGNORE = %r( - (?: - //[^\n\r]*[\n\r]| # line comments - /\* # c-style comments - (?: - [^*/]| # normal chars - /[^*]| # slashes that do not start a nested comment - \*[^/]| # asterisks that do not end this comment - /(?=\*/) # single slash before this comment's end - )* - \*/ # the End of this comment - |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr - )+ - )mx + module Pure + # This class implements the PSON parser that is used to parse a PSON string + # into a Ruby data structure. + class Parser < StringScanner + STRING = /" ((?:[^\x0-\x1f"\\] | + # escaped special characters: + \\["\\\/bfnrt] | + \\u[0-9a-fA-F]{4} | + # match all but escaped special characters: + \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*) + "/nx + INTEGER = /(-?0|-?[1-9]\d*)/ + FLOAT = /(-? + (?:0|[1-9]\d*) + (?: + \.\d+(?i:e[+-]?\d+) | + \.\d+ | + (?i:e[+-]?\d+) + ) + )/x + NAN = /NaN/ + INFINITY = /Infinity/ + MINUS_INFINITY = /-Infinity/ + OBJECT_OPEN = /\{/ + OBJECT_CLOSE = /\}/ + ARRAY_OPEN = /\[/ + ARRAY_CLOSE = /\]/ + PAIR_DELIMITER = /:/ + COLLECTION_DELIMITER = /,/ + TRUE = /true/ + FALSE = /false/ + NULL = /null/ + IGNORE = %r( + (?: + //[^\n\r]*[\n\r]| # line comments + /\* # c-style comments + (?: + [^*/]| # normal chars + /[^*]| # slashes that do not start a nested comment + \*[^/]| # asterisks that do not end this comment + /(?=\*/) # single slash before this comment's end + )* + \*/ # the End of this comment + |[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr + )+ + )mx - UNPARSED = Object.new + UNPARSED = Object.new - # Creates a new PSON::Pure::Parser instance for the string _source_. - # - # It will be configured by the _opts_ hash. _opts_ can have the following - # keys: - # * *max_nesting*: The maximum depth of nesting allowed in the parsed data - # structures. Disable depth checking with :max_nesting => false|nil|0, - # it defaults to 19. - # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in - # defiance of RFC 4627 to be parsed by the Parser. This option defaults - # to false. - # * *create_additions*: If set to false, the Parser doesn't create - # additions even if a matchin class and create_id was found. This option - # defaults to true. - # * *object_class*: Defaults to Hash - # * *array_class*: Defaults to Array - def initialize(source, opts = {}) - super - if !opts.key?(:max_nesting) # defaults to 19 - @max_nesting = 19 - elsif opts[:max_nesting] - @max_nesting = opts[:max_nesting] - else - @max_nesting = 0 - end - @allow_nan = !!opts[:allow_nan] - ca = true - ca = opts[:create_additions] if opts.key?(:create_additions) - @create_id = ca ? PSON.create_id : nil - @object_class = opts[:object_class] || Hash - @array_class = opts[:array_class] || Array - end + # Creates a new PSON::Pure::Parser instance for the string _source_. + # + # It will be configured by the _opts_ hash. _opts_ can have the following + # keys: + # * *max_nesting*: The maximum depth of nesting allowed in the parsed data + # structures. Disable depth checking with :max_nesting => false|nil|0, + # it defaults to 19. + # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in + # defiance of RFC 4627 to be parsed by the Parser. This option defaults + # to false. + # * *create_additions*: If set to false, the Parser doesn't create + # additions even if a matchin class and create_id was found. This option + # defaults to true. + # * *object_class*: Defaults to Hash + # * *array_class*: Defaults to Array + def initialize(source, opts = {}) + super + if !opts.key?(:max_nesting) # defaults to 19 + @max_nesting = 19 + elsif opts[:max_nesting] + @max_nesting = opts[:max_nesting] + else + @max_nesting = 0 + end + @allow_nan = !!opts[:allow_nan] + ca = true + ca = opts[:create_additions] if opts.key?(:create_additions) + @create_id = ca ? PSON.create_id : nil + @object_class = opts[:object_class] || Hash + @array_class = opts[:array_class] || Array + end - alias source string + alias source string - # Parses the current PSON string _source_ and returns the complete data - # structure as a result. - def parse - reset - obj = nil - until eos? - case - when scan(OBJECT_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in PSON!" - @current_nesting = 1 - obj = parse_object - when scan(ARRAY_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in PSON!" - @current_nesting = 1 - obj = parse_array - when skip(IGNORE) - ; - else - raise ParserError, "source '#{peek(20)}' not in PSON!" - end - end - obj or raise ParserError, "source did not contain any PSON!" - obj - end + # Parses the current PSON string _source_ and returns the complete data + # structure as a result. + def parse + reset + obj = nil + until eos? + case + when scan(OBJECT_OPEN) + obj and raise ParserError, "source '#{peek(20)}' not in PSON!" + @current_nesting = 1 + obj = parse_object + when scan(ARRAY_OPEN) + obj and raise ParserError, "source '#{peek(20)}' not in PSON!" + @current_nesting = 1 + obj = parse_array + when skip(IGNORE) + ; + else + raise ParserError, "source '#{peek(20)}' not in PSON!" + end + end + obj or raise ParserError, "source did not contain any PSON!" + obj + end - private + private - # Unescape characters in strings. - UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr } + # Unescape characters in strings. + UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr } - UNESCAPE_MAP.update( - { - ?" => '"', - ?\\ => '\\', - ?/ => '/', - ?b => "\b", - ?f => "\f", - ?n => "\n", - ?r => "\r", - ?t => "\t", - ?u => nil, + UNESCAPE_MAP.update( + { + ?" => '"', + ?\\ => '\\', + ?/ => '/', + ?b => "\b", + ?f => "\f", + ?n => "\n", + ?r => "\r", + ?t => "\t", + ?u => nil, - }) + }) - def parse_string - if scan(STRING) - return '' if self[1].empty? - string = self[1].gsub(%r{(?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff])}n) do |c| - if u = UNESCAPE_MAP[$MATCH[1]] - u - else # \uXXXX - bytes = '' - i = 0 - while c[6 * i] == ?\\ && c[6 * i + 1] == ?u - bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16) - i += 1 - end - PSON::UTF16toUTF8.iconv(bytes) - end - end - string.force_encoding(Encoding::UTF_8) if string.respond_to?(:force_encoding) - string - else - UNPARSED - end - rescue Iconv::Failure => e - raise GeneratorError, "Caught #{e.class}: #{e}" + def parse_string + if scan(STRING) + return '' if self[1].empty? + string = self[1].gsub(%r{(?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff])}n) do |c| + if u = UNESCAPE_MAP[$MATCH[1]] + u + else # \uXXXX + bytes = '' + i = 0 + while c[6 * i] == ?\\ && c[6 * i + 1] == ?u + bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16) + i += 1 + end + PSON::UTF16toUTF8.iconv(bytes) end + end + string.force_encoding(Encoding::UTF_8) if string.respond_to?(:force_encoding) + string + else + UNPARSED + end + rescue Iconv::Failure => e + raise GeneratorError, "Caught #{e.class}: #{e}" + end - def parse_value - case - when scan(FLOAT) - Float(self[1]) - when scan(INTEGER) - Integer(self[1]) - when scan(TRUE) - true - when scan(FALSE) - false - when scan(NULL) - nil - when (string = parse_string) != UNPARSED - string - when scan(ARRAY_OPEN) - @current_nesting += 1 - ary = parse_array - @current_nesting -= 1 - ary - when scan(OBJECT_OPEN) - @current_nesting += 1 - obj = parse_object - @current_nesting -= 1 - obj - when @allow_nan && scan(NAN) - NaN - when @allow_nan && scan(INFINITY) - Infinity - when @allow_nan && scan(MINUS_INFINITY) - MinusInfinity - else - UNPARSED - end - end + def parse_value + case + when scan(FLOAT) + Float(self[1]) + when scan(INTEGER) + Integer(self[1]) + when scan(TRUE) + true + when scan(FALSE) + false + when scan(NULL) + nil + when (string = parse_string) != UNPARSED + string + when scan(ARRAY_OPEN) + @current_nesting += 1 + ary = parse_array + @current_nesting -= 1 + ary + when scan(OBJECT_OPEN) + @current_nesting += 1 + obj = parse_object + @current_nesting -= 1 + obj + when @allow_nan && scan(NAN) + NaN + when @allow_nan && scan(INFINITY) + Infinity + when @allow_nan && scan(MINUS_INFINITY) + MinusInfinity + else + UNPARSED + end + end - def parse_array - raise NestingError, "nesting of #@current_nesting is too deep" if - @max_nesting.nonzero? && @current_nesting > @max_nesting - result = @array_class.new - delim = false - until eos? - case - when (value = parse_value) != UNPARSED - delim = false - result << value - skip(IGNORE) - if scan(COLLECTION_DELIMITER) - delim = true - elsif match?(ARRAY_CLOSE) - ; - else - raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!" - end - when scan(ARRAY_CLOSE) - raise ParserError, "expected next element in array at '#{peek(20)}'!" if delim - break - when skip(IGNORE) - ; - else - raise ParserError, "unexpected token in array at '#{peek(20)}'!" - end - end - result + def parse_array + raise NestingError, "nesting of #@current_nesting is too deep" if + @max_nesting.nonzero? && @current_nesting > @max_nesting + result = @array_class.new + delim = false + until eos? + case + when (value = parse_value) != UNPARSED + delim = false + result << value + skip(IGNORE) + if scan(COLLECTION_DELIMITER) + delim = true + elsif match?(ARRAY_CLOSE) + ; + else + raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!" end + when scan(ARRAY_CLOSE) + raise ParserError, "expected next element in array at '#{peek(20)}'!" if delim + break + when skip(IGNORE) + ; + else + raise ParserError, "unexpected token in array at '#{peek(20)}'!" + end + end + result + end - def parse_object - raise NestingError, "nesting of #@current_nesting is too deep" if - @max_nesting.nonzero? && @current_nesting > @max_nesting - result = @object_class.new - delim = false - until eos? - case - when (string = parse_string) != UNPARSED - skip(IGNORE) - raise ParserError, "expected ':' in object at '#{peek(20)}'!" unless scan(PAIR_DELIMITER) - skip(IGNORE) - unless (value = parse_value).equal? UNPARSED - result[string] = value - delim = false - skip(IGNORE) - if scan(COLLECTION_DELIMITER) - delim = true - elsif match?(OBJECT_CLOSE) - ; - else - raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!" - end - else - raise ParserError, "expected value in object at '#{peek(20)}'!" - end - when scan(OBJECT_CLOSE) - raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!" if delim - if @create_id and klassname = result[@create_id] - klass = PSON.deep_const_get klassname - break unless klass and klass.pson_creatable? - result = klass.pson_create(result) - end - break - when skip(IGNORE) - ; - else - raise ParserError, "unexpected token in object at '#{peek(20)}'!" - end - end - result + def parse_object + raise NestingError, "nesting of #@current_nesting is too deep" if + @max_nesting.nonzero? && @current_nesting > @max_nesting + result = @object_class.new + delim = false + until eos? + case + when (string = parse_string) != UNPARSED + skip(IGNORE) + raise ParserError, "expected ':' in object at '#{peek(20)}'!" unless scan(PAIR_DELIMITER) + skip(IGNORE) + unless (value = parse_value).equal? UNPARSED + result[string] = value + delim = false + skip(IGNORE) + if scan(COLLECTION_DELIMITER) + delim = true + elsif match?(OBJECT_CLOSE) + ; + else + raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!" + end + else + raise ParserError, "expected value in object at '#{peek(20)}'!" + end + when scan(OBJECT_CLOSE) + raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!" if delim + if @create_id and klassname = result[@create_id] + klass = PSON.deep_const_get klassname + break unless klass and klass.pson_creatable? + result = klass.pson_create(result) end + break + when skip(IGNORE) + ; + else + raise ParserError, "unexpected token in object at '#{peek(20)}'!" + end end + result + end end + end end |