repo: init

This commit is contained in:
2024-03-15 19:02:03 +07:00
commit 0a431f0bd8
10 changed files with 426 additions and 0 deletions

4
asjsonp/__init__.py Executable file
View File

@ -0,0 +1,4 @@
from asjsonp.module import loads
__all__ = ["loads"]

4
asjsonp/__main__.py Executable file
View File

@ -0,0 +1,4 @@
from asjsonp.module import loads
print(loads(open("test.json", "r").read()))

125
asjsonp/module.py Executable file
View File

@ -0,0 +1,125 @@
__all__ = ["loads"]
def js2py(obj: str):
"""
Convert a JSON string to a Python object.
This follows the simple approach defined in https://docs.python.org/3/library/json.html#encoders-and-decoders
"""
if obj == "true":
return True
elif obj == "false":
return False
elif obj == "null":
return None
elif obj == "NaN":
return float("nan")
elif obj == "Infinity":
return float("inf")
elif obj == "-Infinity":
return float("-inf")
elif obj.isnumeric():
return int(obj)
elif obj.lower().replace(".", "").replace("e", "").replace("-", "").isnumeric():
return float(obj)
else:
raise ValueError(f"Invalid JSON value: {obj}")
def loads(s: str):
result: list | dict = []
first_open: bool = False
multiple_object_root: bool = False
parents: list = []
current: dict | list = result
dict_current_key: str = None
dict_reading_value: str = None
# Set the value so Pylance can be happy.
current_text: str = ""
is_reading_string: bool = False
# Ignore the rest of the string line if it's a comment
prev_char: str = ""
ignore_rest: bool = False
def handle_reading_value():
nonlocal dict_reading_value
nonlocal dict_current_key
if dict_reading_value is not None and dict_reading_value.strip():
value = js2py(dict_reading_value)
if dict_current_key:
current[dict_current_key] = value
else:
current.append(value)
dict_reading_value = None
dict_current_key = None
for char in s:
if ignore_rest:
if char == "\n":
ignore_rest = False
elif is_reading_string:
# Handle string closing quote
if char == '"':
is_reading_string = False
if isinstance(current, dict):
if not dict_current_key:
dict_current_key = current_text
else:
current[dict_current_key] = current_text
dict_current_key = None
dict_reading_value = None
else:
current.append(current_text)
dict_reading_value = None
current_text = ""
else:
current_text += char
# Handle object & array types
elif char in ["{", "["]:
obj: dict | list
match char:
case "{":
if not first_open:
first_open = True
obj = {}
case "[":
if not first_open:
first_open = True
multiple_object_root = True
obj = []
if isinstance(current, list):
current.append(obj)
elif isinstance(current, dict):
current[dict_current_key] = obj
parents.append(current)
# Reference to the current dict
if isinstance(current, list):
current = current[-1]
elif isinstance(current, dict):
current = current[dict_current_key]
dict_current_key = None
# Handle object and array closing bracket
elif char in ["}", "]"]:
# Switch reference back to the parent dict
handle_reading_value()
current = parents.pop()
# Handle string opening quote
elif char == '"':
is_reading_string = True
current_text = ""
elif char == ":":
dict_reading_value = ""
elif char == ",":
handle_reading_value()
elif char == "/":
if prev_char == "/":
ignore_rest = True
else:
if dict_reading_value is not None:
if char.strip():
dict_reading_value += char
prev_char = char
if not multiple_object_root:
result = result[0]
return result