Last active
August 11, 2021 18:42
-
-
Save fabiommendes/56a111ab54b90362ba6499614c5ced56 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Reader: | |
def __init__(self, src, pos=0): | |
self.src = src.strip() | |
self.pos = pos | |
def read(self, st): | |
""" | |
Lê sub-string "st" na posição atual. | |
""" | |
if not self.src.startswith(st, self.pos): | |
raise SyntaxError(f"espera {st!r}") | |
self.pos += len(st) | |
def ws(self): | |
try: | |
while self.src[self.pos].isspace(): | |
self.pos += 1 | |
except IndexError: | |
pass | |
def check_EOF(self): | |
rest = self.src[self.pos :] | |
if not (rest == "" or rest.isspace()): | |
raise SyntaxError(f"espera EOF, obteve {rest!r}") | |
class JSAtomsMixin(Reader): | |
def read_number(self): | |
pos_end = self.pos | |
while pos_end < len(self.src) and self.src[pos_end].isdigit(): | |
pos_end += 1 | |
n = int(self.src[self.pos : pos_end]) | |
self.pos = pos_end | |
self.ws() | |
return n | |
def read_string(self): | |
pos_end = self.src.find('"', self.pos + 1) | |
st = self.src[self.pos + 1 : pos_end] | |
self.pos = pos_end + 1 | |
self.ws() | |
return st | |
class JSONReader(JSAtomsMixin, Reader): | |
def read_value(self): | |
if self.src.startswith("true", self.pos): | |
self.pos += 4 | |
self.ws() | |
return True | |
elif self.src.startswith("false", self.pos): | |
self.pos += 5 | |
self.ws() | |
return False | |
elif self.src.startswith("null", self.pos): | |
self.pos += 4 | |
self.ws() | |
return None | |
elif self.src[self.pos].isdigit(): | |
return self.read_number() | |
elif self.src[self.pos] == '"': | |
return self.read_string() | |
elif self.src[self.pos] == "[": | |
return self.read_array() | |
elif self.src[self.pos] == "{": | |
return self.read_object() | |
else: | |
raise SyntaxError(f"unexpected {self.src[self.pos:]!r}") | |
def read_array(self): | |
self.pos += 1 | |
self.ws() | |
if self.src[self.pos] == "]": | |
self.pos += 1 | |
self.ws() | |
return [] | |
elements = [self.read_value()] | |
while True: | |
if self.src[self.pos] == "]": | |
self.pos += 1 | |
self.ws() | |
return elements | |
self.read(",") | |
self.ws() | |
elements.append(self.read_value()) | |
def read_object(self): | |
self.pos += 1 | |
self.ws() | |
if self.src[self.pos] == "}": | |
self.pos += 1 | |
self.ws() | |
return {} | |
elements = [self.read_pair()] | |
while True: | |
if self.src[self.pos] == "}": | |
self.pos += 1 | |
self.ws() | |
return dict(elements) | |
self.read(",") | |
self.ws() | |
elements.append(self.read_pair()) | |
def read_pair(self): | |
key = self.read_string() | |
self.ws() | |
self.read(":") | |
self.ws() | |
value = self.read_value() | |
return (key, value) | |
def loads(text: str) -> object: | |
""" | |
Carrega um documento JSON e retorna o valor Python correspondente. | |
""" | |
reader = JSONReader(text) | |
reader.ws() | |
value = reader.read_value() | |
reader.check_EOF() | |
return value | |
# Exemplos | |
# 1) Implementar suporte a comentários estilo C/JS | |
# Obs: // este é um comentário | |
# /* este também é um comentário */ | |
# | |
# 2) Aceitar uma vírgula após os últimos | |
# elementos de uma array ou objeto | |
print(loads(" false ")) | |
print(loads(" null ")) | |
print(loads(" 42 ")) | |
print(loads(' "Hello World" ')) | |
print(loads("[ true ,false ,null ]")) | |
print(loads('{ "answer" : [1,2,3] }')) | |
print(loads("true // valor booleano em json")) | |
print(loads("true /* valor booleano em json */")) | |
print(loads("[true,false,null,]")) | |
print(loads('{"x": 1, "y": 2,}')) | |
""" | |
[ {"nome": "Guido", "idade": 50} | |
, {"nome": "Alan Kay", "idade": 60} | |
, {"nome": "Ada", "idade": 160} | |
] | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment