JsonhPy 2.0__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.0 → jsonhpy-2.3}/PKG-INFO +2 -2
- {jsonhpy-2.0 → jsonhpy-2.3}/README.md +1 -1
- {jsonhpy-2.0 → jsonhpy-2.3}/pyproject.toml +1 -1
- {jsonhpy-2.0 → jsonhpy-2.3}/src/JsonhPy/JsonhPy.py +85 -70
- {jsonhpy-2.0 → jsonhpy-2.3}/src/JsonhPy.egg-info/PKG-INFO +2 -2
- {jsonhpy-2.0 → jsonhpy-2.3}/LICENSE.md +0 -0
- {jsonhpy-2.0 → jsonhpy-2.3}/setup.cfg +0 -0
- {jsonhpy-2.0 → jsonhpy-2.3}/src/JsonhPy/__init__.py +0 -0
- {jsonhpy-2.0 → jsonhpy-2.3}/src/JsonhPy.egg-info/SOURCES.txt +0 -0
- {jsonhpy-2.0 → jsonhpy-2.3}/src/JsonhPy.egg-info/dependency_links.txt +0 -0
- {jsonhpy-2.0 → jsonhpy-2.3}/src/JsonhPy.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: JsonhPy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3
|
|
4
4
|
Summary: JSON for Humans in Python.
|
|
5
5
|
Author-email: Joyless <joyless.mod@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -69,5 +69,5 @@ jsonh: str = """
|
|
|
69
69
|
this is: awesome
|
|
70
70
|
}
|
|
71
71
|
"""
|
|
72
|
-
json:
|
|
72
|
+
json: object = JsonhReader.parse_element_from_string(jsonh).value()
|
|
73
73
|
```
|
|
@@ -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:
|
|
@@ -541,10 +549,11 @@ class JsonhReader:
|
|
|
541
549
|
next_element = parse_next_element()
|
|
542
550
|
|
|
543
551
|
# Ensure exactly one element
|
|
544
|
-
if
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
552
|
+
if not next_element.is_error:
|
|
553
|
+
if self.options.parse_single_element:
|
|
554
|
+
for token in self.read_end_of_elements():
|
|
555
|
+
if token.is_error:
|
|
556
|
+
return JsonhResult.from_error(token.error())
|
|
548
557
|
|
|
549
558
|
return next_element
|
|
550
559
|
|
|
@@ -1262,7 +1271,7 @@ class JsonhReader:
|
|
|
1262
1271
|
is_empty = False
|
|
1263
1272
|
# Dot
|
|
1264
1273
|
elif next == '.':
|
|
1265
|
-
# Disallow dot
|
|
1274
|
+
# Disallow dot following underscore
|
|
1266
1275
|
if len(number_builder.ref) >= 1 and number_builder.ref[-1] == '_':
|
|
1267
1276
|
return JsonhResult.from_error("`.` must not follow `_` in number")
|
|
1268
1277
|
|
|
@@ -1436,26 +1445,40 @@ class JsonhReader:
|
|
|
1436
1445
|
return
|
|
1437
1446
|
|
|
1438
1447
|
def _read_hex_sequence(self, length: int) -> JsonhResult[int, str]:
|
|
1439
|
-
|
|
1448
|
+
assert(length <= 8)
|
|
1449
|
+
|
|
1450
|
+
value: int = 0
|
|
1440
1451
|
|
|
1441
|
-
for
|
|
1452
|
+
for _ in range(0, length):
|
|
1442
1453
|
next: str | None = self._read()
|
|
1443
1454
|
|
|
1444
1455
|
# Hex digit
|
|
1445
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'))):
|
|
1446
|
-
|
|
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
|
|
1447
1466
|
# Unexpected char
|
|
1448
1467
|
else:
|
|
1449
1468
|
return JsonhResult.from_error("Incorrect number of hexadecimal digits in unicode escape sequence")
|
|
1450
1469
|
|
|
1451
|
-
#
|
|
1452
|
-
return JsonhResult.from_value(
|
|
1470
|
+
# Return aggregated value
|
|
1471
|
+
return JsonhResult.from_value(value)
|
|
1453
1472
|
|
|
1454
|
-
def _read_escape_sequence(self) -> JsonhResult[str, str]:
|
|
1473
|
+
def _read_escape_sequence(self, high_surrogate: int | None = None) -> JsonhResult[str, str]:
|
|
1455
1474
|
escape_char: str | None = self._read()
|
|
1456
1475
|
if escape_char == None:
|
|
1457
1476
|
return JsonhResult.from_error("Expected escape sequence, got end of input")
|
|
1458
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
|
+
|
|
1459
1482
|
match escape_char:
|
|
1460
1483
|
# Reverse solidus
|
|
1461
1484
|
case '\\':
|
|
@@ -1489,13 +1512,13 @@ class JsonhReader:
|
|
|
1489
1512
|
return JsonhResult.from_value('\u001b')
|
|
1490
1513
|
# Unicode hex sequence
|
|
1491
1514
|
case 'u':
|
|
1492
|
-
return self._read_hex_escape_sequence(4)
|
|
1515
|
+
return self._read_hex_escape_sequence(4, high_surrogate)
|
|
1493
1516
|
# Short unicode hex sequence
|
|
1494
1517
|
case 'x':
|
|
1495
|
-
return self._read_hex_escape_sequence(2)
|
|
1518
|
+
return self._read_hex_escape_sequence(2, high_surrogate)
|
|
1496
1519
|
# Long unicode hex sequence
|
|
1497
1520
|
case 'U':
|
|
1498
|
-
return self._read_hex_escape_sequence(8)
|
|
1521
|
+
return self._read_hex_escape_sequence(8, high_surrogate)
|
|
1499
1522
|
# Escaped newline
|
|
1500
1523
|
case self._NEWLINE_CHARS:
|
|
1501
1524
|
# Join CR LF
|
|
@@ -1506,51 +1529,43 @@ class JsonhReader:
|
|
|
1506
1529
|
case _:
|
|
1507
1530
|
return JsonhResult.from_value(escape_char)
|
|
1508
1531
|
|
|
1509
|
-
def _read_hex_escape_sequence(self, length: int) -> JsonhResult[str, str]:
|
|
1510
|
-
# This method is used to combine escaped UTF-16 surrogate pairs (e.g. "\uD83D\uDC7D" -> "👽")
|
|
1511
|
-
|
|
1512
|
-
# Read hex digits & convert to uint
|
|
1532
|
+
def _read_hex_escape_sequence(self, length: int, high_surrogate: int | None) -> JsonhResult[str, str]:
|
|
1513
1533
|
code_point: JsonhResult[int, str] = self._read_hex_sequence(length)
|
|
1514
1534
|
if code_point.is_error:
|
|
1515
1535
|
return JsonhResult.from_error(code_point.error())
|
|
1516
1536
|
|
|
1517
|
-
#
|
|
1518
|
-
if
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
case 'x':
|
|
1531
|
-
low_code_point = self._read_hex_sequence(2)
|
|
1532
|
-
case 'U':
|
|
1533
|
-
low_code_point = self._read_hex_sequence(8)
|
|
1534
|
-
# Ensure hex sequence read successfully
|
|
1535
|
-
if low_code_point.is_error:
|
|
1536
|
-
return JsonhResult.from_error(low_code_point.error())
|
|
1537
|
-
# Combine high and low surrogates
|
|
1538
|
-
code_point.value_or_none = self._utf16_surrogates_to_code_point(code_point.value(), low_code_point.value())
|
|
1539
|
-
# Other escape sequence
|
|
1540
|
-
else:
|
|
1541
|
-
self.index = original_position
|
|
1542
|
-
|
|
1543
|
-
# Rune
|
|
1544
|
-
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()))
|
|
1545
1550
|
|
|
1546
1551
|
@staticmethod
|
|
1547
|
-
def _utf16_surrogates_to_code_point(high_surrogate: int, low_surrogate: int) -> int:
|
|
1548
|
-
|
|
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)))
|
|
1549
1560
|
|
|
1550
1561
|
@staticmethod
|
|
1551
1562
|
def _is_utf16_high_surrogate(code_point: int) -> bool:
|
|
1552
1563
|
return code_point >= 0xD800 and code_point <= 0xDBFF
|
|
1553
1564
|
|
|
1565
|
+
@staticmethod
|
|
1566
|
+
def _is_utf16_low_surrogate(code_point: int) -> bool:
|
|
1567
|
+
return code_point >= 0xDC00 and code_point <= 0xDFFF
|
|
1568
|
+
|
|
1554
1569
|
def _peek(self) -> str | None:
|
|
1555
1570
|
if self.index >= len(self.string):
|
|
1556
1571
|
return None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: JsonhPy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3
|
|
4
4
|
Summary: JSON for Humans in Python.
|
|
5
5
|
Author-email: Joyless <joyless.mod@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -69,5 +69,5 @@ jsonh: str = """
|
|
|
69
69
|
this is: awesome
|
|
70
70
|
}
|
|
71
71
|
"""
|
|
72
|
-
json:
|
|
72
|
+
json: object = JsonhReader.parse_element_from_string(jsonh).value()
|
|
73
73
|
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|