JsonhPy 2.2__tar.gz → 2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jsonhpy-2.2 → jsonhpy-2.3}/PKG-INFO +1 -1
- {jsonhpy-2.2 → jsonhpy-2.3}/pyproject.toml +1 -1
- {jsonhpy-2.2 → jsonhpy-2.3}/src/JsonhPy/JsonhPy.py +80 -66
- {jsonhpy-2.2 → jsonhpy-2.3}/src/JsonhPy.egg-info/PKG-INFO +1 -1
- {jsonhpy-2.2 → jsonhpy-2.3}/LICENSE.md +0 -0
- {jsonhpy-2.2 → jsonhpy-2.3}/README.md +0 -0
- {jsonhpy-2.2 → jsonhpy-2.3}/setup.cfg +0 -0
- {jsonhpy-2.2 → jsonhpy-2.3}/src/JsonhPy/__init__.py +0 -0
- {jsonhpy-2.2 → jsonhpy-2.3}/src/JsonhPy.egg-info/SOURCES.txt +0 -0
- {jsonhpy-2.2 → jsonhpy-2.3}/src/JsonhPy.egg-info/dependency_links.txt +0 -0
- {jsonhpy-2.2 → jsonhpy-2.3}/src/JsonhPy.egg-info/top_level.txt +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import math
|
|
1
2
|
from enum import Enum
|
|
2
3
|
from typing import Iterator, Iterable
|
|
3
4
|
|
|
@@ -223,7 +224,15 @@ class JsonhNumberParser:
|
|
|
223
224
|
return exponent
|
|
224
225
|
|
|
225
226
|
# Multiply mantissa by 10 ^ exponent
|
|
226
|
-
|
|
227
|
+
try:
|
|
228
|
+
return JsonhResult.from_value(mantissa.value() * (10.0 ** exponent.value()))
|
|
229
|
+
except:
|
|
230
|
+
if mantissa.value() > 0:
|
|
231
|
+
return JsonhResult.from_value(math.inf)
|
|
232
|
+
elif mantissa.value() < 0:
|
|
233
|
+
return JsonhResult.from_value(-math.inf)
|
|
234
|
+
else:
|
|
235
|
+
return JsonhResult.from_value(0.0)
|
|
227
236
|
|
|
228
237
|
@staticmethod
|
|
229
238
|
def _parse_fractional_number(digits: str, base_digits: str) -> JsonhResult[float, str]:
|
|
@@ -238,29 +247,32 @@ class JsonhNumberParser:
|
|
|
238
247
|
|
|
239
248
|
# Get parts of number
|
|
240
249
|
whole_part: str = digits[:dot_index]
|
|
241
|
-
|
|
250
|
+
fraction_part: str = digits[(dot_index + 1):]
|
|
242
251
|
|
|
243
252
|
# Parse parts of number
|
|
244
|
-
whole: JsonhResult[
|
|
253
|
+
whole: JsonhResult[float, str] = JsonhNumberParser._parse_whole_number(whole_part, base_digits)
|
|
245
254
|
if whole.is_error:
|
|
246
255
|
return whole
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
|
|
257
|
+
# Add each column of fraction digits
|
|
258
|
+
fraction: float = 0.0
|
|
259
|
+
for index in range(len(fraction_part) - 1, -1, -1):
|
|
260
|
+
# Get current digit
|
|
261
|
+
digit_char: str = fraction_part[index]
|
|
262
|
+
digit_int: int = base_digits.find(digit_char.lower())
|
|
263
|
+
|
|
264
|
+
# Ensure digit is valid
|
|
265
|
+
if digit_int < 0:
|
|
266
|
+
return JsonhResult.from_error(f"Invalid digit: '{digit_char}'")
|
|
267
|
+
|
|
268
|
+
# Add value of column
|
|
269
|
+
fraction = (fraction + digit_int) / len(base_digits)
|
|
258
270
|
|
|
259
271
|
# Combine whole and fraction
|
|
260
|
-
return JsonhResult.from_value(
|
|
272
|
+
return JsonhResult.from_value(whole.value() + fraction)
|
|
261
273
|
|
|
262
274
|
@staticmethod
|
|
263
|
-
def _parse_whole_number(digits: str, base_digits: str) -> JsonhResult[
|
|
275
|
+
def _parse_whole_number(digits: str, base_digits: str) -> JsonhResult[float, str]:
|
|
264
276
|
"""
|
|
265
277
|
Converts a whole number (e.g. `12345`) from the given base (e.g. `01234567`) to a base-10 integer.
|
|
266
278
|
"""
|
|
@@ -274,7 +286,7 @@ class JsonhNumberParser:
|
|
|
274
286
|
digits = digits[1:]
|
|
275
287
|
|
|
276
288
|
# Add each column of digits
|
|
277
|
-
integer:
|
|
289
|
+
integer: float = 0.0
|
|
278
290
|
for index in range(0, len(digits)):
|
|
279
291
|
# Get current digit
|
|
280
292
|
digit_char: str = digits[index]
|
|
@@ -284,12 +296,8 @@ class JsonhNumberParser:
|
|
|
284
296
|
if digit_int < 0:
|
|
285
297
|
return JsonhResult.from_error(f"Invalid digit: '{digit_char}'")
|
|
286
298
|
|
|
287
|
-
# Get magnitude of current digit column
|
|
288
|
-
column_number: int = len(digits) - 1 - index
|
|
289
|
-
column_magnitude: int = len(base_digits) ** column_number
|
|
290
|
-
|
|
291
299
|
# Add value of column
|
|
292
|
-
integer
|
|
300
|
+
integer = (integer * len(base_digits)) + digit_int
|
|
293
301
|
|
|
294
302
|
# Apply sign
|
|
295
303
|
if sign != 1:
|
|
@@ -1263,7 +1271,7 @@ class JsonhReader:
|
|
|
1263
1271
|
is_empty = False
|
|
1264
1272
|
# Dot
|
|
1265
1273
|
elif next == '.':
|
|
1266
|
-
# Disallow dot
|
|
1274
|
+
# Disallow dot following underscore
|
|
1267
1275
|
if len(number_builder.ref) >= 1 and number_builder.ref[-1] == '_':
|
|
1268
1276
|
return JsonhResult.from_error("`.` must not follow `_` in number")
|
|
1269
1277
|
|
|
@@ -1437,26 +1445,40 @@ class JsonhReader:
|
|
|
1437
1445
|
return
|
|
1438
1446
|
|
|
1439
1447
|
def _read_hex_sequence(self, length: int) -> JsonhResult[int, str]:
|
|
1440
|
-
|
|
1448
|
+
assert(length <= 8)
|
|
1441
1449
|
|
|
1442
|
-
|
|
1450
|
+
value: int = 0
|
|
1451
|
+
|
|
1452
|
+
for _ in range(0, length):
|
|
1443
1453
|
next: str | None = self._read()
|
|
1444
1454
|
|
|
1445
1455
|
# Hex digit
|
|
1446
1456
|
if next != None and ((ord('0') <= ord(next) <= ord('9')) or (ord('A') <= ord(next) <= ord('F')) or (ord('a') <= ord(next) <= ord('f'))):
|
|
1447
|
-
|
|
1457
|
+
# Get hex digit
|
|
1458
|
+
digit: int = ord(next)
|
|
1459
|
+
# Convert hex digit to integer
|
|
1460
|
+
integer: int = \
|
|
1461
|
+
digit - ord('A') + 10 if (digit >= ord('A') and digit <= ord('F')) else \
|
|
1462
|
+
digit - ord('a') + 10 if (digit >= ord('a') and digit <= ord('f')) else \
|
|
1463
|
+
digit - ord('0')
|
|
1464
|
+
# Aggregate digit into value
|
|
1465
|
+
value = (value * 16) + integer
|
|
1448
1466
|
# Unexpected char
|
|
1449
1467
|
else:
|
|
1450
1468
|
return JsonhResult.from_error("Incorrect number of hexadecimal digits in unicode escape sequence")
|
|
1451
1469
|
|
|
1452
|
-
#
|
|
1453
|
-
return JsonhResult.from_value(
|
|
1470
|
+
# Return aggregated value
|
|
1471
|
+
return JsonhResult.from_value(value)
|
|
1454
1472
|
|
|
1455
|
-
def _read_escape_sequence(self) -> JsonhResult[str, str]:
|
|
1473
|
+
def _read_escape_sequence(self, high_surrogate: int | None = None) -> JsonhResult[str, str]:
|
|
1456
1474
|
escape_char: str | None = self._read()
|
|
1457
1475
|
if escape_char == None:
|
|
1458
1476
|
return JsonhResult.from_error("Expected escape sequence, got end of input")
|
|
1459
1477
|
|
|
1478
|
+
# Ensure high surrogates are completed
|
|
1479
|
+
if high_surrogate != None and escape_char not in ['u', 'x', 'U']:
|
|
1480
|
+
return JsonhResult.from_error("Expected low surrogate after high surrogate")
|
|
1481
|
+
|
|
1460
1482
|
match escape_char:
|
|
1461
1483
|
# Reverse solidus
|
|
1462
1484
|
case '\\':
|
|
@@ -1490,13 +1512,13 @@ class JsonhReader:
|
|
|
1490
1512
|
return JsonhResult.from_value('\u001b')
|
|
1491
1513
|
# Unicode hex sequence
|
|
1492
1514
|
case 'u':
|
|
1493
|
-
return self._read_hex_escape_sequence(4)
|
|
1515
|
+
return self._read_hex_escape_sequence(4, high_surrogate)
|
|
1494
1516
|
# Short unicode hex sequence
|
|
1495
1517
|
case 'x':
|
|
1496
|
-
return self._read_hex_escape_sequence(2)
|
|
1518
|
+
return self._read_hex_escape_sequence(2, high_surrogate)
|
|
1497
1519
|
# Long unicode hex sequence
|
|
1498
1520
|
case 'U':
|
|
1499
|
-
return self._read_hex_escape_sequence(8)
|
|
1521
|
+
return self._read_hex_escape_sequence(8, high_surrogate)
|
|
1500
1522
|
# Escaped newline
|
|
1501
1523
|
case self._NEWLINE_CHARS:
|
|
1502
1524
|
# Join CR LF
|
|
@@ -1507,51 +1529,43 @@ class JsonhReader:
|
|
|
1507
1529
|
case _:
|
|
1508
1530
|
return JsonhResult.from_value(escape_char)
|
|
1509
1531
|
|
|
1510
|
-
def _read_hex_escape_sequence(self, length: int) -> JsonhResult[str, str]:
|
|
1511
|
-
# This method is used to combine escaped UTF-16 surrogate pairs (e.g. "\uD83D\uDC7D" -> "👽")
|
|
1512
|
-
|
|
1513
|
-
# Read hex digits & convert to uint
|
|
1532
|
+
def _read_hex_escape_sequence(self, length: int, high_surrogate: int | None) -> JsonhResult[str, str]:
|
|
1514
1533
|
code_point: JsonhResult[int, str] = self._read_hex_sequence(length)
|
|
1515
1534
|
if code_point.is_error:
|
|
1516
1535
|
return JsonhResult.from_error(code_point.error())
|
|
1517
1536
|
|
|
1518
|
-
#
|
|
1519
|
-
if
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
case 'x':
|
|
1532
|
-
low_code_point = self._read_hex_sequence(2)
|
|
1533
|
-
case 'U':
|
|
1534
|
-
low_code_point = self._read_hex_sequence(8)
|
|
1535
|
-
# Ensure hex sequence read successfully
|
|
1536
|
-
if low_code_point.is_error:
|
|
1537
|
-
return JsonhResult.from_error(low_code_point.error())
|
|
1538
|
-
# Combine high and low surrogates
|
|
1539
|
-
code_point.value_or_none = self._utf16_surrogates_to_code_point(code_point.value(), low_code_point.value())
|
|
1540
|
-
# Other escape sequence
|
|
1541
|
-
else:
|
|
1542
|
-
self.index = original_position
|
|
1543
|
-
|
|
1544
|
-
# Rune
|
|
1545
|
-
return JsonhResult.from_value(chr(code_point.value()))
|
|
1537
|
+
# Low surrogate
|
|
1538
|
+
if high_surrogate != None:
|
|
1539
|
+
combined: JsonhResult[int, str] = self._utf16_surrogates_to_code_point(high_surrogate, code_point.value())
|
|
1540
|
+
if combined.is_error:
|
|
1541
|
+
return JsonhResult.from_error(combined.error())
|
|
1542
|
+
return JsonhResult.from_value(chr(combined.value()))
|
|
1543
|
+
else:
|
|
1544
|
+
# High surrogate followed by low surrogate
|
|
1545
|
+
if self._is_utf16_high_surrogate(code_point.value()) and self._read_one('\\'):
|
|
1546
|
+
return self._read_escape_sequence(code_point.value())
|
|
1547
|
+
# Standalone character
|
|
1548
|
+
else:
|
|
1549
|
+
return JsonhResult.from_value(chr(code_point.value()))
|
|
1546
1550
|
|
|
1547
1551
|
@staticmethod
|
|
1548
|
-
def _utf16_surrogates_to_code_point(high_surrogate: int, low_surrogate: int) -> int:
|
|
1549
|
-
|
|
1552
|
+
def _utf16_surrogates_to_code_point(high_surrogate: int, low_surrogate: int) -> JsonhResult[int, str]:
|
|
1553
|
+
if not JsonhReader._is_utf16_high_surrogate(high_surrogate):
|
|
1554
|
+
return JsonhResult.from_error("High surrogate out of range")
|
|
1555
|
+
|
|
1556
|
+
if not JsonhReader._is_utf16_low_surrogate(low_surrogate):
|
|
1557
|
+
return JsonhResult.from_error("Low surrogate out of range")
|
|
1558
|
+
|
|
1559
|
+
return JsonhResult.from_value(0x10000 + (((high_surrogate - 0xD800) << 10) | (low_surrogate - 0xDC00)))
|
|
1550
1560
|
|
|
1551
1561
|
@staticmethod
|
|
1552
1562
|
def _is_utf16_high_surrogate(code_point: int) -> bool:
|
|
1553
1563
|
return code_point >= 0xD800 and code_point <= 0xDBFF
|
|
1554
1564
|
|
|
1565
|
+
@staticmethod
|
|
1566
|
+
def _is_utf16_low_surrogate(code_point: int) -> bool:
|
|
1567
|
+
return code_point >= 0xDC00 and code_point <= 0xDFFF
|
|
1568
|
+
|
|
1555
1569
|
def _peek(self) -> str | None:
|
|
1556
1570
|
if self.index >= len(self.string):
|
|
1557
1571
|
return None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|