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