| Class | JSON::Pure::Parser |
| In: |
lib/json/pure/parser.rb
|
| Parent: | StringScanner |
| STRING | = | /" ((?:[^\x0-\x1f"\\] | \\["\\\/bfnrt] | \\u[0-9a-fA-F]{4} | \\[\x20-\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 64
64: def initialize(source, opts = {})
65: super
66: if !opts.key?(:max_nesting) # defaults to 19
67: @max_nesting = 19
68: elsif opts[:max_nesting]
69: @max_nesting = opts[:max_nesting]
70: else
71: @max_nesting = 0
72: end
73: @allow_nan = !!opts[:allow_nan]
74: ca = true
75: ca = opts[:create_additions] if opts.key?(:create_additions)
76: @create_id = ca ? JSON.create_id : nil
77: end
Parses the current JSON string source and returns the complete data structure as a result.
# File lib/json/pure/parser.rb, line 83
83: def parse
84: reset
85: obj = nil
86: until eos?
87: case
88: when scan(OBJECT_OPEN)
89: obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
90: @current_nesting = 1
91: obj = parse_object
92: when scan(ARRAY_OPEN)
93: obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
94: @current_nesting = 1
95: obj = parse_array
96: when skip(IGNORE)
97: ;
98: else
99: raise ParserError, "source '#{peek(20)}' not in JSON!"
100: end
101: end
102: obj or raise ParserError, "source did not contain any JSON!"
103: obj
104: end
# File lib/json/pure/parser.rb, line 180
180: def parse_array
181: raise NestingError, "nesting of #@current_nesting is to deep" if
182: @max_nesting.nonzero? && @current_nesting > @max_nesting
183: result = []
184: delim = false
185: until eos?
186: case
187: when (value = parse_value) != UNPARSED
188: delim = false
189: result << value
190: skip(IGNORE)
191: if scan(COLLECTION_DELIMITER)
192: delim = true
193: elsif match?(ARRAY_CLOSE)
194: ;
195: else
196: raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
197: end
198: when scan(ARRAY_CLOSE)
199: if delim
200: raise ParserError, "expected next element in array at '#{peek(20)}'!"
201: end
202: break
203: when skip(IGNORE)
204: ;
205: else
206: raise ParserError, "unexpected token in array at '#{peek(20)}'!"
207: end
208: end
209: result
210: end
# File lib/json/pure/parser.rb, line 212
212: def parse_object
213: raise NestingError, "nesting of #@current_nesting is to deep" if
214: @max_nesting.nonzero? && @current_nesting > @max_nesting
215: result = {}
216: delim = false
217: until eos?
218: case
219: when (string = parse_string) != UNPARSED
220: skip(IGNORE)
221: unless scan(PAIR_DELIMITER)
222: raise ParserError, "expected ':' in object at '#{peek(20)}'!"
223: end
224: skip(IGNORE)
225: unless (value = parse_value).equal? UNPARSED
226: result[string] = value
227: delim = false
228: skip(IGNORE)
229: if scan(COLLECTION_DELIMITER)
230: delim = true
231: elsif match?(OBJECT_CLOSE)
232: ;
233: else
234: raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
235: end
236: else
237: raise ParserError, "expected value in object at '#{peek(20)}'!"
238: end
239: when scan(OBJECT_CLOSE)
240: if delim
241: raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
242: end
243: if @create_id and klassname = result[@create_id]
244: klass = JSON.deep_const_get klassname
245: break unless klass and klass.json_creatable?
246: result = klass.json_create(result)
247: end
248: break
249: when skip(IGNORE)
250: ;
251: else
252: raise ParserError, "unexpected token in object at '#{peek(20)}'!"
253: end
254: end
255: result
256: end
# File lib/json/pure/parser.rb, line 122
122: def parse_string
123: if scan(STRING)
124: return '' if self[1].empty?
125: self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
126: if u = UNESCAPE_MAP[c[1]]
127: u
128: else # \uXXXX
129: bytes = ''
130: i = 0
131: while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
132: bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
133: i += 1
134: end
135: JSON::UTF16toUTF8.iconv(bytes)
136: end
137: end
138: else
139: UNPARSED
140: end
141: rescue Iconv::Failure => e
142: raise GeneratorError, "Caught #{e.class}: #{e}"
143: end
# File lib/json/pure/parser.rb, line 145
145: def parse_value
146: case
147: when scan(FLOAT)
148: Float(self[1])
149: when scan(INTEGER)
150: Integer(self[1])
151: when scan(TRUE)
152: true
153: when scan(FALSE)
154: false
155: when scan(NULL)
156: nil
157: when (string = parse_string) != UNPARSED
158: string
159: when scan(ARRAY_OPEN)
160: @current_nesting += 1
161: ary = parse_array
162: @current_nesting -= 1
163: ary
164: when scan(OBJECT_OPEN)
165: @current_nesting += 1
166: obj = parse_object
167: @current_nesting -= 1
168: obj
169: when @allow_nan && scan(NAN)
170: NaN
171: when @allow_nan && scan(INFINITY)
172: Infinity
173: when @allow_nan && scan(MINUS_INFINITY)
174: MinusInfinity
175: else
176: UNPARSED
177: end
178: end