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