Class JSON::Pure::Parser
In: lib/json/pure/parser.rb
Parent: StringScanner
ParserError NestingError GeneratorError CircularDatastructure StandardError JSONError MissingUnicodeSupport Gtk StringScanner Parser State lib/json/common.rb Ext Editor lib/json/pure/parser.rb lib/json/pure/generator.rb Integer FalseClass Array Hash Float NilClass Object TrueClass Extend String GeneratorMethods Generator Pure JSON dot/m_9_0.png

This class implements the JSON parser that is used to parse a JSON string into a Ruby data structure.

Methods

Constants

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.

External Aliases

string -> source

Public Class methods

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:

  • 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.

[Source]

    # 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

Public Instance methods

Parses the current JSON string source and returns the complete data structure as a result.

[Source]

    # 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

Private Instance methods

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Validate]