Skip to content

Instantly share code, notes, and snippets.

@fabiommendes
Last active August 11, 2021 18:42
Show Gist options
  • Save fabiommendes/56a111ab54b90362ba6499614c5ced56 to your computer and use it in GitHub Desktop.
Save fabiommendes/56a111ab54b90362ba6499614c5ced56 to your computer and use it in GitHub Desktop.
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