| Class | JSON::Pure::Parser |
| In: |
lib/json/pure/parser.rb
|
| Parent: | 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 | ||
| UNESCAPE_MAP | = | Hash.new { |h, k| h[k] = k.chr } | Unescape characters in strings. |
| string | -> | source |
Creates a new JSON::Pure::Parser instance for the string source.
It will be configured by the opts hash. opts can have the following keys:
# File lib/json/pure/parser.rb, line 68
68: def initialize(source, opts = {})
69: super
70: if !opts.key?(:max_nesting) # defaults to 19
71: @max_nesting = 19
72: elsif opts[:max_nesting]
73: @max_nesting = opts[:max_nesting]
74: else
75: @max_nesting = 0
76: end
77: @allow_nan = !!opts[:allow_nan]
78: ca = false
79: ca = opts[:create_additions] if opts.key?(:create_additions)
80: @create_id = ca ? JSON.create_id : nil
81: @object_class = opts[:object_class] || Hash
82: @array_class = opts[:array_class] || Array
83: end
Parses the current JSON string source and returns the complete data structure as a result.
# File lib/json/pure/parser.rb, line 89
89: def parse
90: reset
91: obj = nil
92: until eos?
93: case
94: when scan(OBJECT_OPEN)
95: obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
96: @current_nesting = 1
97: obj = parse_object
98: when scan(ARRAY_OPEN)
99: obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
100: @current_nesting = 1
101: obj = parse_array
102: when skip(IGNORE)
103: ;
104: else
105: raise ParserError, "source '#{peek(20)}' not in JSON!"
106: end
107: end
108: obj or raise ParserError, "source did not contain any JSON!"
109: obj
110: end
# File lib/json/pure/parser.rb, line 190
190: def parse_array
191: raise NestingError, "nesting of #@current_nesting is too deep" if
192: @max_nesting.nonzero? && @current_nesting > @max_nesting
193: result = @array_class.new
194: delim = false
195: until eos?
196: case
197: when (value = parse_value) != UNPARSED
198: delim = false
199: result << value
200: skip(IGNORE)
201: if scan(COLLECTION_DELIMITER)
202: delim = true
203: elsif match?(ARRAY_CLOSE)
204: ;
205: else
206: raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
207: end
208: when scan(ARRAY_CLOSE)
209: if delim
210: raise ParserError, "expected next element in array at '#{peek(20)}'!"
211: end
212: break
213: when skip(IGNORE)
214: ;
215: else
216: raise ParserError, "unexpected token in array at '#{peek(20)}'!"
217: end
218: end
219: result
220: end
# File lib/json/pure/parser.rb, line 222
222: def parse_object
223: raise NestingError, "nesting of #@current_nesting is too deep" if
224: @max_nesting.nonzero? && @current_nesting > @max_nesting
225: result = @object_class.new
226: delim = false
227: until eos?
228: case
229: when (string = parse_string) != UNPARSED
230: skip(IGNORE)
231: unless scan(PAIR_DELIMITER)
232: raise ParserError, "expected ':' in object at '#{peek(20)}'!"
233: end
234: skip(IGNORE)
235: unless (value = parse_value).equal? UNPARSED
236: result[string] = value
237: delim = false
238: skip(IGNORE)
239: if scan(COLLECTION_DELIMITER)
240: delim = true
241: elsif match?(OBJECT_CLOSE)
242: ;
243: else
244: raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
245: end
246: else
247: raise ParserError, "expected value in object at '#{peek(20)}'!"
248: end
249: when scan(OBJECT_CLOSE)
250: if delim
251: raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
252: end
253: if @create_id and klassname = result[@create_id]
254: klass = JSON.deep_const_get klassname
255: break unless klass and klass.json_creatable?
256: result = klass.json_create(result)
257: end
258: break
259: when skip(IGNORE)
260: ;
261: else
262: raise ParserError, "unexpected token in object at '#{peek(20)}'!"
263: end
264: end
265: result
266: end
# File lib/json/pure/parser.rb, line 128
128: def parse_string
129: if scan(STRING)
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]]
133: u
134: else # \uXXXX
135: bytes = ''
136: i = 0
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)
139: i += 1
140: end
141: JSON::UTF16toUTF8.iconv(bytes)
142: end
143: end
144: if string.respond_to?(:force_encoding)
145: string.force_encoding(Encoding::UTF_8)
146: end
147: string
148: else
149: UNPARSED
150: end
151: rescue Iconv::Failure => e
152: raise GeneratorError, "Caught #{e.class}: #{e}"
153: end
# File lib/json/pure/parser.rb, line 155
155: def parse_value
156: case
157: when scan(FLOAT)
158: Float(self[1])
159: when scan(INTEGER)
160: Integer(self[1])
161: when scan(TRUE)
162: true
163: when scan(FALSE)
164: false
165: when scan(NULL)
166: nil
167: when (string = parse_string) != UNPARSED
168: string
169: when scan(ARRAY_OPEN)
170: @current_nesting += 1
171: ary = parse_array
172: @current_nesting -= 1
173: ary
174: when scan(OBJECT_OPEN)
175: @current_nesting += 1
176: obj = parse_object
177: @current_nesting -= 1
178: obj
179: when @allow_nan && scan(NAN)
180: NaN
181: when @allow_nan && scan(INFINITY)
182: Infinity
183: when @allow_nan && scan(MINUS_INFINITY)
184: MinusInfinity
185: else
186: UNPARSED
187: end
188: end