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