Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

"""JSON token scanner 

""" 

import re 

def _import_c_make_scanner(): 

try: 

from simplejson._speedups import make_scanner 

return make_scanner 

except ImportError: 

return None 

c_make_scanner = _import_c_make_scanner() 

 

__all__ = ['make_scanner', 'JSONDecodeError'] 

 

NUMBER_RE = re.compile( 

r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?', 

(re.VERBOSE | re.MULTILINE | re.DOTALL)) 

 

class JSONDecodeError(ValueError): 

"""Subclass of ValueError with the following additional properties: 

 

msg: The unformatted error message 

doc: The JSON document being parsed 

pos: The start index of doc where parsing failed 

end: The end index of doc where parsing failed (may be None) 

lineno: The line corresponding to pos 

colno: The column corresponding to pos 

endlineno: The line corresponding to end (may be None) 

endcolno: The column corresponding to end (may be None) 

 

""" 

# Note that this exception is used from _speedups 

def __init__(self, msg, doc, pos, end=None): 

ValueError.__init__(self, errmsg(msg, doc, pos, end=end)) 

self.msg = msg 

self.doc = doc 

self.pos = pos 

self.end = end 

self.lineno, self.colno = linecol(doc, pos) 

if end is not None: 

self.endlineno, self.endcolno = linecol(doc, end) 

else: 

self.endlineno, self.endcolno = None, None 

 

def __reduce__(self): 

return self.__class__, (self.msg, self.doc, self.pos, self.end) 

 

 

def linecol(doc, pos): 

lineno = doc.count('\n', 0, pos) + 1 

if lineno == 1: 

colno = pos + 1 

else: 

colno = pos - doc.rindex('\n', 0, pos) 

return lineno, colno 

 

 

def errmsg(msg, doc, pos, end=None): 

lineno, colno = linecol(doc, pos) 

msg = msg.replace('%r', repr(doc[pos:pos + 1])) 

if end is None: 

fmt = '%s: line %d column %d (char %d)' 

return fmt % (msg, lineno, colno, pos) 

endlineno, endcolno = linecol(doc, end) 

fmt = '%s: line %d column %d - line %d column %d (char %d - %d)' 

return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end) 

 

 

def py_make_scanner(context): 

parse_object = context.parse_object 

parse_array = context.parse_array 

parse_string = context.parse_string 

match_number = NUMBER_RE.match 

encoding = context.encoding 

strict = context.strict 

parse_float = context.parse_float 

parse_int = context.parse_int 

parse_constant = context.parse_constant 

object_hook = context.object_hook 

object_pairs_hook = context.object_pairs_hook 

memo = context.memo 

 

def _scan_once(string, idx): 

errmsg = 'Expecting value' 

try: 

nextchar = string[idx] 

except IndexError: 

raise JSONDecodeError(errmsg, string, idx) 

 

if nextchar == '"': 

return parse_string(string, idx + 1, encoding, strict) 

elif nextchar == '{': 

return parse_object((string, idx + 1), encoding, strict, 

_scan_once, object_hook, object_pairs_hook, memo) 

elif nextchar == '[': 

return parse_array((string, idx + 1), _scan_once) 

elif nextchar == 'n' and string[idx:idx + 4] == 'null': 

return None, idx + 4 

elif nextchar == 't' and string[idx:idx + 4] == 'true': 

return True, idx + 4 

elif nextchar == 'f' and string[idx:idx + 5] == 'false': 

return False, idx + 5 

 

m = match_number(string, idx) 

if m is not None: 

integer, frac, exp = m.groups() 

if frac or exp: 

res = parse_float(integer + (frac or '') + (exp or '')) 

else: 

res = parse_int(integer) 

return res, m.end() 

elif nextchar == 'N' and string[idx:idx + 3] == 'NaN': 

return parse_constant('NaN'), idx + 3 

elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity': 

return parse_constant('Infinity'), idx + 8 

elif nextchar == '-' and string[idx:idx + 9] == '-Infinity': 

return parse_constant('-Infinity'), idx + 9 

else: 

raise JSONDecodeError(errmsg, string, idx) 

 

def scan_once(string, idx): 

if idx < 0: 

# Ensure the same behavior as the C speedup, otherwise 

# this would work for *some* negative string indices due 

# to the behavior of __getitem__ for strings. #98 

raise JSONDecodeError('Expecting value', string, idx) 

try: 

return _scan_once(string, idx) 

finally: 

memo.clear() 

 

return scan_once 

 

make_scanner = c_make_scanner or py_make_scanner