Initial commit
This commit is contained in:
		
							
								
								
									
										1
									
								
								json_py/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								json_py/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
from json_py.json_py import *
 | 
			
		||||
							
								
								
									
										19
									
								
								json_py/json_py.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								json_py/json_py.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
import typing
 | 
			
		||||
import json_py.lexer as lexer
 | 
			
		||||
import json_py.parser as parser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_json(obj: typing.Any) -> str:
 | 
			
		||||
    raise NotImplementedError("Not implemented yet")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def from_json(string: str) -> typing.Any:
 | 
			
		||||
    tokens = lexer.lex(string)
 | 
			
		||||
    return parser.parse(tokens)[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "to_json",
 | 
			
		||||
    "from_json",
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										97
									
								
								json_py/lexer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								json_py/lexer.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
import typing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TypeLexer = typing.Tuple[typing.Optional[typing.Any], str]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lex_string(string: str) -> TypeLexer:
 | 
			
		||||
    if not string.startswith('"'):
 | 
			
		||||
        return None, string
 | 
			
		||||
 | 
			
		||||
    string = string[1:]
 | 
			
		||||
    for i in range(len(string)):
 | 
			
		||||
        if string[i] == '"':
 | 
			
		||||
            return string[:i], string[i + 1 :]
 | 
			
		||||
 | 
			
		||||
    return None, string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lex_number(string: str) -> TypeLexer:
 | 
			
		||||
    if not string[0].isdigit():
 | 
			
		||||
        return None, string
 | 
			
		||||
 | 
			
		||||
    has_decimal = False
 | 
			
		||||
 | 
			
		||||
    for i in range(len(string)):
 | 
			
		||||
        if string[i] == ".":
 | 
			
		||||
            if has_decimal:
 | 
			
		||||
                raise ValueError("Invalid number")
 | 
			
		||||
 | 
			
		||||
            has_decimal = True
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        if not string[i].isdigit():
 | 
			
		||||
            if has_decimal:
 | 
			
		||||
                return float(string[:i]), string[i:]
 | 
			
		||||
            return int(string[:i]), string[i:]
 | 
			
		||||
 | 
			
		||||
    if has_decimal:
 | 
			
		||||
        return float(string), ""
 | 
			
		||||
    return int(string), ""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lex_bool(string: str) -> TypeLexer:
 | 
			
		||||
    if string[0].lower() not in "tf":
 | 
			
		||||
        return None, string
 | 
			
		||||
 | 
			
		||||
    if string[:4].lower() == "true":
 | 
			
		||||
        return True, string[4:]
 | 
			
		||||
    elif string[:5].lower() == "false":
 | 
			
		||||
        return False, string[5:]
 | 
			
		||||
 | 
			
		||||
    return None, string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lex_null(string: str) -> TypeLexer:
 | 
			
		||||
    if string[:4].lower() == "null":
 | 
			
		||||
        return True, string[4:]
 | 
			
		||||
 | 
			
		||||
    return None, string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TokenList = typing.List[typing.Any]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lex(string: str) -> TokenList:
 | 
			
		||||
    tokens: TokenList = []
 | 
			
		||||
    while len(string) > 0:
 | 
			
		||||
        json_string, string = lex_string(string)
 | 
			
		||||
        if json_string is not None:
 | 
			
		||||
            tokens.append(json_string)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        json_number, string = lex_number(string)
 | 
			
		||||
        if json_number is not None:
 | 
			
		||||
            tokens.append(json_number)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        json_bool, string = lex_bool(string)
 | 
			
		||||
        if json_bool is not None:
 | 
			
		||||
            tokens.append(json_bool)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        json_null, string = lex_null(string)
 | 
			
		||||
        if json_null is not None:
 | 
			
		||||
            tokens.append(None)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        if string[0] in " ":
 | 
			
		||||
            string = string[1:]
 | 
			
		||||
        elif string[0] in ":{},[]":
 | 
			
		||||
            tokens.append(string[0])
 | 
			
		||||
            string = string[1:]
 | 
			
		||||
        else:
 | 
			
		||||
            raise Exception("Unexpected character: {}".format(string[0]))
 | 
			
		||||
 | 
			
		||||
    return tokens
 | 
			
		||||
							
								
								
									
										73
									
								
								json_py/parser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								json_py/parser.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
import typing
 | 
			
		||||
import json_py.lexer as lexer
 | 
			
		||||
 | 
			
		||||
ParserResult = typing.Tuple[typing.Any, lexer.TokenList]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_array(tokens: lexer.TokenList) -> ParserResult:
 | 
			
		||||
    json_array: typing.List[typing.Any] = []
 | 
			
		||||
 | 
			
		||||
    if tokens[0] == "]":
 | 
			
		||||
        return json_array, tokens[1:]
 | 
			
		||||
 | 
			
		||||
    expect_comma = False
 | 
			
		||||
    for i in range(len(tokens)):
 | 
			
		||||
        t = tokens[i]
 | 
			
		||||
        if t == "]":
 | 
			
		||||
            if not expect_comma:
 | 
			
		||||
                raise ValueError("Expected one more item")
 | 
			
		||||
 | 
			
		||||
            return json_array, tokens[i + 1 :]
 | 
			
		||||
        elif t == ",":
 | 
			
		||||
            if not expect_comma:
 | 
			
		||||
                raise ValueError("Unexpected comma")
 | 
			
		||||
 | 
			
		||||
            expect_comma = False
 | 
			
		||||
        else:
 | 
			
		||||
            if expect_comma:
 | 
			
		||||
                raise ValueError("Expected comma but got item")
 | 
			
		||||
 | 
			
		||||
            json_array.append(t)
 | 
			
		||||
            expect_comma = True
 | 
			
		||||
 | 
			
		||||
    raise ValueError("List not closed")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_object(tokens: lexer.TokenList) -> ParserResult:
 | 
			
		||||
    json_object: typing.Any = {}
 | 
			
		||||
 | 
			
		||||
    if tokens[0] == "}":
 | 
			
		||||
        return json_object, tokens[1:]
 | 
			
		||||
 | 
			
		||||
    is_syntax: typing.Callable[[str], bool] = lambda x: str(x) in ":"
 | 
			
		||||
    while True:
 | 
			
		||||
        json_key = tokens[0]
 | 
			
		||||
 | 
			
		||||
        if is_syntax(json_key):
 | 
			
		||||
            raise Exception(f"Expected value before '{json_key}'")
 | 
			
		||||
 | 
			
		||||
        colon = tokens[1]
 | 
			
		||||
        if colon != ":":
 | 
			
		||||
            raise Exception(f"Expected ':' but got '{colon}'")
 | 
			
		||||
 | 
			
		||||
        json_value, tokens = parse(tokens[2:])
 | 
			
		||||
        json_object[json_key] = json_value
 | 
			
		||||
 | 
			
		||||
        next_token = tokens[0]
 | 
			
		||||
        if next_token == ",":
 | 
			
		||||
            tokens = tokens[1:]
 | 
			
		||||
        elif next_token == "}":
 | 
			
		||||
            return json_object, tokens[1:]
 | 
			
		||||
        else:
 | 
			
		||||
            raise Exception(f"Expected ',' or '{'}'}' but got '{next_token}'")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse(tokens: lexer.TokenList) -> typing.Any:
 | 
			
		||||
    t = tokens[0]
 | 
			
		||||
    if t == "[":
 | 
			
		||||
        return parse_array(tokens[1:])
 | 
			
		||||
    elif t == "{":
 | 
			
		||||
        return parse_object(tokens[1:])
 | 
			
		||||
    else:
 | 
			
		||||
        return t, tokens[1:]
 | 
			
		||||
		Reference in New Issue
	
	Block a user