5
# This class implements the PSON parser that is used to parse a PSON string
6
# into a Ruby data structure.
7
class Parser < StringScanner
8
STRING = /" ((?:[^\x0-\x1f"\\] |
9
# escaped special characters:
12
# match all but escaped special characters:
13
\\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
15
INTEGER = /(-?0|-?[1-9]\d*)/
26
MINUS_INFINITY = /-Infinity/
32
COLLECTION_DELIMITER = /,/
38
//[^\n\r]*[\n\r]| # line comments
39
/\* # c-style comments
42
/[^*]| # slashes that do not start a nested comment
43
\*[^/]| # asterisks that do not end this comment
44
/(?=\*/) # single slash before this comment's end
46
\*/ # the End of this comment
47
|[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
53
# Creates a new PSON::Pure::Parser instance for the string _source_.
55
# It will be configured by the _opts_ hash. _opts_ can have the following
57
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
58
# structures. Disable depth checking with :max_nesting => false|nil|0,
60
# * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
61
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
63
# * *create_additions*: If set to false, the Parser doesn't create
64
# additions even if a matchin class and create_id was found. This option
66
# * *object_class*: Defaults to Hash
67
# * *array_class*: Defaults to Array
68
def initialize(source, opts = {})
70
if !opts.key?(:max_nesting) # defaults to 19
72
elsif opts[:max_nesting]
73
@max_nesting = opts[:max_nesting]
77
@allow_nan = !!opts[:allow_nan]
79
ca = opts[:create_additions] if opts.key?(:create_additions)
80
@create_id = ca ? PSON.create_id : nil
81
@object_class = opts[:object_class] || Hash
82
@array_class = opts[:array_class] || Array
87
# Parses the current PSON string _source_ and returns the complete data
88
# structure as a result.
94
when scan(OBJECT_OPEN)
95
obj and raise ParserError, "source '#{peek(20)}' not in PSON!"
99
obj and raise ParserError, "source '#{peek(20)}' not in PSON!"
105
raise ParserError, "source '#{peek(20)}' not in PSON!"
108
obj or raise ParserError, "source did not contain any PSON!"
114
# Unescape characters in strings.
115
UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
116
UNESCAPE_MAP.update({
130
return '' if self[1].empty?
131
string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
132
if u = UNESCAPE_MAP[$&[1]]
137
while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
138
bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
141
PSON::UTF16toUTF8.iconv(bytes)
144
if string.respond_to?(:force_encoding)
145
string.force_encoding(Encoding::UTF_8)
151
rescue Iconv::Failure => e
152
raise GeneratorError, "Caught #{e.class}: #{e}"
167
when (string = parse_string) != UNPARSED
169
when scan(ARRAY_OPEN)
170
@current_nesting += 1
172
@current_nesting -= 1
174
when scan(OBJECT_OPEN)
175
@current_nesting += 1
177
@current_nesting -= 1
179
when @allow_nan && scan(NAN)
181
when @allow_nan && scan(INFINITY)
183
when @allow_nan && scan(MINUS_INFINITY)
191
raise NestingError, "nesting of #@current_nesting is too deep" if
192
@max_nesting.nonzero? && @current_nesting > @max_nesting
193
result = @array_class.new
197
when (value = parse_value) != UNPARSED
201
if scan(COLLECTION_DELIMITER)
203
elsif match?(ARRAY_CLOSE)
206
raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
208
when scan(ARRAY_CLOSE)
210
raise ParserError, "expected next element in array at '#{peek(20)}'!"
216
raise ParserError, "unexpected token in array at '#{peek(20)}'!"
223
raise NestingError, "nesting of #@current_nesting is too deep" if
224
@max_nesting.nonzero? && @current_nesting > @max_nesting
225
result = @object_class.new
229
when (string = parse_string) != UNPARSED
231
unless scan(PAIR_DELIMITER)
232
raise ParserError, "expected ':' in object at '#{peek(20)}'!"
235
unless (value = parse_value).equal? UNPARSED
236
result[string] = value
239
if scan(COLLECTION_DELIMITER)
241
elsif match?(OBJECT_CLOSE)
244
raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
247
raise ParserError, "expected value in object at '#{peek(20)}'!"
249
when scan(OBJECT_CLOSE)
251
raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
253
if @create_id and klassname = result[@create_id]
254
klass = PSON.deep_const_get klassname
255
break unless klass and klass.pson_creatable?
256
result = klass.pson_create(result)
262
raise ParserError, "unexpected token in object at '#{peek(20)}'!"