class PSON::Pure::Parser
This class implements the PSON
parser that is used to parse a PSON
string into a Ruby data structure.
Constants
- ARRAY_CLOSE
- ARRAY_OPEN
- COLLECTION_DELIMITER
- FALSE
- FLOAT
- IGNORE
- INFINITY
- INTEGER
- MINUS_INFINITY
- NAN
- NULL
- OBJECT_CLOSE
- OBJECT_OPEN
- PAIR_DELIMITER
- STRING
- TRUE
- UNESCAPE_MAP
Unescape characters in strings.
- UNPARSED
Public Class Methods
new(source, opts = {})
click to toggle source
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. -
object_class: Defaults to Hash
-
array_class: Defaults to Array
Calls superclass method
# File lib/puppet/external/pson/pure/parser.rb 65 def initialize(source, opts = {}) 66 source = convert_encoding source 67 super source 68 if !opts.key?(:max_nesting) # defaults to 19 69 @max_nesting = 19 70 elsif opts[:max_nesting] 71 @max_nesting = opts[:max_nesting] 72 else 73 @max_nesting = 0 74 end 75 @allow_nan = !!opts[:allow_nan] 76 @object_class = opts[:object_class] || Hash 77 @array_class = opts[:array_class] || Array 78 end
Public Instance Methods
parse()
click to toggle source
Parses the current PSON
string source and returns the complete data structure as a result.
# File lib/puppet/external/pson/pure/parser.rb 84 def parse 85 reset 86 obj = nil 87 until eos? 88 case 89 when scan(OBJECT_OPEN) 90 obj and raise ParserError, "source '#{peek(20)}' not in PSON!" 91 @current_nesting = 1 92 obj = parse_object 93 when scan(ARRAY_OPEN) 94 obj and raise ParserError, "source '#{peek(20)}' not in PSON!" 95 @current_nesting = 1 96 obj = parse_array 97 when skip(IGNORE) 98 ; 99 else 100 raise ParserError, "source '#{peek(20)}' not in PSON!" 101 end 102 end 103 obj or raise ParserError, "source did not contain any PSON!" 104 obj 105 end
Private Instance Methods
convert_encoding(source)
click to toggle source
# File lib/puppet/external/pson/pure/parser.rb 109 def convert_encoding(source) 110 if source.respond_to?(:to_str) 111 source = source.to_str 112 else 113 raise TypeError, "#{source.inspect} is not like a string" 114 end 115 if supports_encodings?(source) 116 if source.encoding == ::Encoding::ASCII_8BIT 117 b = source[0, 4].bytes.to_a 118 source = 119 case 120 when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 121 source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) 122 when b.size >= 4 && b[0] == 0 && b[2] == 0 123 source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) 124 when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 125 source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) 126 when b.size >= 4 && b[1] == 0 && b[3] == 0 127 source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) 128 else 129 source.dup 130 end 131 else 132 source = source.encode(::Encoding::UTF_8) 133 end 134 source.force_encoding(::Encoding::ASCII_8BIT) 135 else 136 b = source 137 source = 138 case 139 when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 140 PSON.encode('utf-8', 'utf-32be', b) 141 when b.size >= 4 && b[0] == 0 && b[2] == 0 142 PSON.encode('utf-8', 'utf-16be', b) 143 when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 144 PSON.encode('utf-8', 'utf-32le', b) 145 when b.size >= 4 && b[1] == 0 && b[3] == 0 146 PSON.encode('utf-8', 'utf-16le', b) 147 else 148 b 149 end 150 end 151 source 152 end
parse_array()
click to toggle source
# File lib/puppet/external/pson/pure/parser.rb 240 def parse_array 241 raise NestingError, "nesting of #@current_nesting is too deep" if 242 @max_nesting.nonzero? && @current_nesting > @max_nesting 243 result = @array_class.new 244 delim = false 245 until eos? 246 case 247 when (value = parse_value) != UNPARSED 248 delim = false 249 result << value 250 skip(IGNORE) 251 if scan(COLLECTION_DELIMITER) 252 delim = true 253 elsif match?(ARRAY_CLOSE) 254 ; 255 else 256 raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!" 257 end 258 when scan(ARRAY_CLOSE) 259 raise ParserError, "expected next element in array at '#{peek(20)}'!" if delim 260 break 261 when skip(IGNORE) 262 ; 263 else 264 raise ParserError, "unexpected token in array at '#{peek(20)}'!" 265 end 266 end 267 result 268 end
parse_object()
click to toggle source
# File lib/puppet/external/pson/pure/parser.rb 270 def parse_object 271 raise NestingError, "nesting of #@current_nesting is too deep" if 272 @max_nesting.nonzero? && @current_nesting > @max_nesting 273 result = @object_class.new 274 delim = false 275 until eos? 276 case 277 when (string = parse_string) != UNPARSED 278 skip(IGNORE) 279 raise ParserError, "expected ':' in object at '#{peek(20)}'!" unless scan(PAIR_DELIMITER) 280 skip(IGNORE) 281 unless (value = parse_value).equal? UNPARSED 282 result[string] = value 283 delim = false 284 skip(IGNORE) 285 if scan(COLLECTION_DELIMITER) 286 delim = true 287 elsif match?(OBJECT_CLOSE) 288 ; 289 else 290 raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!" 291 end 292 else 293 raise ParserError, "expected value in object at '#{peek(20)}'!" 294 end 295 when scan(OBJECT_CLOSE) 296 raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!" if delim 297 break 298 when skip(IGNORE) 299 ; 300 else 301 raise ParserError, "unexpected token in object at '#{peek(20)}'!" 302 end 303 end 304 result 305 end
parse_string()
click to toggle source
# File lib/puppet/external/pson/pure/parser.rb 179 def parse_string 180 if scan(STRING) 181 return '' if self[1].empty? 182 string = self[1].gsub(%r{(?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff])}n) do |c| 183 u = UNESCAPE_MAP[$MATCH[1]] 184 if u 185 u 186 else # \uXXXX 187 bytes = '' 188 i = 0 189 while c[6 * i] == ?\\ && c[6 * i + 1] == ?u 190 bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16) 191 i += 1 192 end 193 PSON.encode('utf-8', 'utf-16be', bytes) 194 end 195 end 196 string.force_encoding(Encoding::UTF_8) if string.respond_to?(:force_encoding) 197 string 198 else 199 UNPARSED 200 end 201 rescue => e 202 raise GeneratorError, "Caught #{e.class}: #{e}", e.backtrace 203 end
parse_value()
click to toggle source
# File lib/puppet/external/pson/pure/parser.rb 205 def parse_value 206 case 207 when scan(FLOAT) 208 Float(self[1]) 209 when scan(INTEGER) 210 Integer(self[1]) 211 when scan(TRUE) 212 true 213 when scan(FALSE) 214 false 215 when scan(NULL) 216 nil 217 when (string = parse_string) != UNPARSED 218 string 219 when scan(ARRAY_OPEN) 220 @current_nesting += 1 221 ary = parse_array 222 @current_nesting -= 1 223 ary 224 when scan(OBJECT_OPEN) 225 @current_nesting += 1 226 obj = parse_object 227 @current_nesting -= 1 228 obj 229 when @allow_nan && scan(NAN) 230 NaN 231 when @allow_nan && scan(INFINITY) 232 Infinity 233 when @allow_nan && scan(MINUS_INFINITY) 234 MinusInfinity 235 else 236 UNPARSED 237 end 238 end
supports_encodings?(string)
click to toggle source
# File lib/puppet/external/pson/pure/parser.rb 154 def supports_encodings?(string) 155 # Some modules, such as REXML on 1.8.7 (see #22804) can actually create 156 # a top-level Encoding constant when they are misused. Therefore 157 # checking for just that constant is not enough, so we'll be a bit more 158 # robust about if we can actually support encoding transformations. 159 string.respond_to?(:encoding) && defined?(::Encoding) 160 end