1"""JSON token scanner
2"""
3import re
4from .errors import JSONDecodeError
5
6def _import_c_make_scanner():
7 try:
8 from ._speedups import make_scanner
9 return make_scanner
10 except ImportError:
11 return None
12c_make_scanner = _import_c_make_scanner()
13
14__all__ = ['make_scanner', 'JSONDecodeError']
15
16NUMBER_RE = re.compile(
17 r'(-?(?:0|[1-9][0-9]*))(\.[0-9]+)?([eE][-+]?[0-9]+)?',
18 (re.VERBOSE | re.MULTILINE | re.DOTALL))
19
20
21def py_make_scanner(context):
22 parse_object = context.parse_object
23 parse_array = context.parse_array
24 parse_string = context.parse_string
25 match_number = NUMBER_RE.match
26 encoding = context.encoding
27 strict = context.strict
28 parse_float = context.parse_float
29 parse_int = context.parse_int
30 parse_constant = context.parse_constant
31 object_hook = context.object_hook
32 object_pairs_hook = context.object_pairs_hook
33 array_hook = context.array_hook
34 memo = context.memo
35
36 def _scan_once(string, idx):
37 errmsg = 'Expecting value'
38 try:
39 nextchar = string[idx]
40 except IndexError:
41 raise JSONDecodeError(errmsg, string, idx)
42
43 if nextchar == '"':
44 return parse_string(string, idx + 1, encoding, strict)
45 elif nextchar == '{':
46 return parse_object((string, idx + 1), encoding, strict,
47 _scan_once, object_hook, object_pairs_hook, memo)
48 elif nextchar == '[':
49 return parse_array((string, idx + 1), _scan_once, array_hook)
50 elif nextchar == 'n' and string[idx:idx + 4] == 'null':
51 return None, idx + 4
52 elif nextchar == 't' and string[idx:idx + 4] == 'true':
53 return True, idx + 4
54 elif nextchar == 'f' and string[idx:idx + 5] == 'false':
55 return False, idx + 5
56
57 m = match_number(string, idx)
58 if m is not None:
59 integer, frac, exp = m.groups()
60 if frac or exp:
61 res = parse_float(integer + (frac or '') + (exp or ''))
62 else:
63 res = parse_int(integer)
64 return res, m.end()
65 elif parse_constant and nextchar == 'N' and string[idx:idx + 3] == 'NaN':
66 return parse_constant('NaN'), idx + 3
67 elif parse_constant and nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
68 return parse_constant('Infinity'), idx + 8
69 elif parse_constant and nextchar == '-' and string[idx:idx + 9] == '-Infinity':
70 return parse_constant('-Infinity'), idx + 9
71 else:
72 raise JSONDecodeError(errmsg, string, idx)
73
74 def scan_once(string, idx):
75 if idx < 0:
76 # Ensure the same behavior as the C speedup, otherwise
77 # this would work for *some* negative string indices due
78 # to the behavior of __getitem__ for strings. #98
79 raise JSONDecodeError('Expecting value', string, idx)
80 try:
81 return _scan_once(string, idx)
82 finally:
83 memo.clear()
84
85 return scan_once
86
87make_scanner = c_make_scanner or py_make_scanner