kuroko/modules/json.krk

175 lines
5.4 KiB
Plaintext

"""
JSON parser
"""
def loads(s):
let i = 0
let value # okay dumb scoping thing, but we'll assign this to _value later...
def peek():
return s[i] if i < len(s) else None
def advance():
i++
def whitespace():
while peek() in ' \r\n\t': advance()
def string():
if peek() != '"': raise ValueError("String should start with \" but got " + peek())
advance()
let out = ""
let c = peek()
while c != '"':
if c is None: raise ValueError("Unterminated string")
if c == '\\':
advance()
c = peek()
if c == '"': out += '"'
elif c == '\\': out += '\\'
elif c == '/': out += '/'
elif c == 'b': out += '\x08' # Backspace
elif c == 'f': out += '\x0c' # Formfeed
elif c == 'n': out += '\n'
elif c == 'r': out += '\r'
elif c == 't': out += '\t'
elif c == 'u':
advance()
let sub = [0,0,0,0]
sub[0] = peek()
advance()
sub[1] = peek()
advance()
sub[2] = peek()
advance()
sub[3] = peek()
if not all([x in '0123456789abcdefABCDEF' for x in sub]):
raise ValueError('Invalid codepoint in JSON string')
out += chr(int(sub,16))
else:
raise ValueError('Invalid escape sequence in JSON string')
else:
out += c
advance()
c = peek()
if peek() != '"':
raise ValueError("That was not expected.")
advance()
return out
def obj():
if peek() != '{': raise ValueError("Object should start with {?")
advance()
let out = {}
whitespace()
if peek() == '}': return out
while True:
whitespace()
let k = string()
whitespace()
if peek() != ':': raise ValueError("Expected : after key, got " + peek())
advance()
let v = value()
out[k] = v
if peek() == '}':
advance()
return out
if peek() != ',':
raise ValueError('Expected , after value, or } for end of object.')
advance()
def array():
if peek() != '[': raise ValueError("Array should start with [?")
advance()
let out = []
whitespace()
if peek() == ']': return out
while True:
whitespace()
out.append(value())
if peek() == ']':
advance()
return out
if peek() != ',':
raise ValueError("Expected , after value, or ] for end of array.")
advance()
def number():
let value = 0
let sign = 1
if peek() == '-':
sign = -1
advance()
if peek() == '0':
advance()
elif peek() in '0123456789':
value = int(peek())
advance()
while peek() in '0123456789':
value = value * 10
value = value + int(peek())
advance()
else:
raise ValueError("Expected digit")
if peek() == '.':
advance()
let mult = 0.1
if peek() not in '0123456789':
raise ValueError("Expected digit")
while peek() in '0123456789':
value = value + multiplier * int(peek())
multiplier = multiplier * 0.1
advance()
if peek() in 'eE':
raise NotImplementedError("Exponent values are not implemented.")
return value
def boolean():
let inv = ValueError("Invalid literal while parsing bool")
if peek() == 't':
advance()
if peek() != 'r': raise inv
advance()
if peek() != 'u': raise inv
advance()
if peek() != 'e': raise inv
advance()
return True
elif peek() == 'f':
advance()
if peek() != 'a': raise inv
advance()
if peek() != 'l': raise inv
advance()
if peek() != 's': raise inv
advance()
if peek() != 'e': raise inv
advance()
return False
raise inv
def null():
let inv = ValueError("Invalid literal while parsing null")
if peek() != 'n': raise inv
advance()
if peek() != 'u': raise inv
advance()
if peek() != 'l': raise inv
advance()
if peek() != 'l': raise inv
advance()
return None
def _value():
whitespace()
let c = peek()
let out = None
if c== '"':
out = string()
elif c == '{':
out = obj()
elif c == '[':
out = array()
elif c == '-' or c in '0123456789':
out = number()
elif c == 't' or c == 'f':
out = boolean()
elif c == 'n':
out = null()
else:
raise ValueError("Unexpected character")
whitespace()
return out
value = _value
return value()