364 lines
12 KiB
GDScript
364 lines
12 KiB
GDScript
class_name Json
|
|
|
|
static func stringify(value: Variant, allow_eval: bool = false) -> String:
|
|
var ser = Json.Serializer.new(allow_eval)
|
|
Serde.serialize(ser, value, allow_eval)
|
|
return ser.output
|
|
|
|
static func parse_string(value: String, into: Variant = null, allow_eval: bool = false) -> Variant:
|
|
var de = Json.Deserializer.from_string(value)
|
|
if into != null and into.has_method(Serde.DeserializeMethod):
|
|
return into.call(Serde.DeserializeMethod, de)
|
|
else:
|
|
return de.deserialize_any(Serde.GenericVisitor.new())
|
|
|
|
class SerializeSeq extends Serde.SerializeSeq:
|
|
var ser: Json.Serializer
|
|
var first: bool = true
|
|
|
|
func _init(ser: Json.Serializer) -> void:
|
|
self.ser = ser
|
|
ser.write('[')
|
|
|
|
func serialize_element(value: Variant) -> Result:
|
|
if not first: ser.write(',')
|
|
first = false
|
|
Serde.serialize(ser, value, ser.allow_eval)
|
|
return Result.Unit
|
|
|
|
func end() -> Result:
|
|
ser.write(']')
|
|
return Result.Unit
|
|
|
|
class SerializeMap extends Serde.SerializeMap:
|
|
var ser: Json.Serializer
|
|
var first: bool = true
|
|
|
|
func _init(ser: Json.Serializer) -> void:
|
|
self.ser = ser
|
|
ser.write('{')
|
|
|
|
func serialize_key(value: Variant) -> Result:
|
|
if not first: ser.write(',')
|
|
first = false
|
|
Serde.serialize(ser, value, ser.allow_eval)
|
|
return Result.Unit
|
|
|
|
func serialize_value(value: Variant) -> Result:
|
|
ser.write(':')
|
|
Serde.serialize(ser, value, ser.allow_eval)
|
|
return Result.Unit
|
|
|
|
func end() -> Result:
|
|
ser.write('}')
|
|
return Result.Unit
|
|
|
|
class Serializer extends Serde.Serializer:
|
|
var output: String
|
|
var allow_eval: bool
|
|
|
|
func _init(allow_eval: bool = false) -> void:
|
|
self.allow_eval = allow_eval
|
|
|
|
func write(value: String) -> Result:
|
|
output += value
|
|
return Result.Unit
|
|
|
|
func write_string(value: Variant) -> Result:
|
|
return write(str(value))
|
|
|
|
func serialize_nil() -> Result:
|
|
return write("null")
|
|
|
|
func serialize_bool(value: bool) -> Result:
|
|
return write_string(value)
|
|
|
|
func serialize_int(value: int):
|
|
return write_string(value)
|
|
|
|
func serialize_float(value: float) -> Result:
|
|
return write_string(value)
|
|
|
|
func serialize_string(value: String):
|
|
return write('"%s"' % value)
|
|
|
|
func serialize_seq(_len: int):
|
|
return Json.SerializeSeq.new(self)
|
|
|
|
func serialize_dict(_len: int):
|
|
return Json.SerializeMap.new(self)
|
|
|
|
func serialize_object(_name: StringName, len: int):
|
|
return serialize_dict(len)
|
|
|
|
class Parser extends RefCounted:
|
|
var de: Json.Deserializer
|
|
var iter: PeekableIter
|
|
|
|
func _init(de: Json.Deserializer) -> void:
|
|
self.de = de
|
|
iter = de._iter
|
|
|
|
static func eq(a: Variant, b: Variant) -> bool:
|
|
return a == b
|
|
|
|
static func _join(value: Array) -> String:
|
|
return "".join(value)
|
|
|
|
static func _collect(results: Array[Result]) -> Result:
|
|
return Result.collect_ok(results).map(_join)
|
|
|
|
func any() -> Result:
|
|
match iter.peek():
|
|
var x when x.is_some():
|
|
return Result.ok(iter.next().unwrap())
|
|
_: return Result.err(Error.new("eof"))
|
|
|
|
func char(ch: String) -> Result:
|
|
match iter.peek():
|
|
var x when x.filter(eq.bind(ch)).is_some(): return Result.ok(iter.next().unwrap())
|
|
var x: return Result.err(Error.new('expecting "%s", got "%s"' % [ch, x.unwrap()]))
|
|
|
|
func str(value: String) -> Result:
|
|
for i in range(0, value.length()):
|
|
var ch = self.char(value[i])
|
|
if ch.is_err(): return ch
|
|
return Result.ok(value)
|
|
|
|
func any_of(parsers: Array[Callable]) -> Result:
|
|
for parser in parsers:
|
|
var result = parser.call()
|
|
if result.is_ok(): return result
|
|
return Result.err(Error.new('none of the provided parsers matched input'))
|
|
|
|
func all_of(parsers: Array) -> Result:
|
|
var results: Array[String] = []
|
|
for parser in parsers:
|
|
match parser.call():
|
|
var x when x.is_ok(): results.append(x.unwrap())
|
|
var x: return x
|
|
return Result.ok(results)
|
|
|
|
func many(parser: Callable, limit: int = 1000) -> Result:
|
|
var results: Array[String] = []
|
|
var value = parser.call()
|
|
|
|
while value.is_ok() and limit > 0:
|
|
results.append(value.unwrap())
|
|
value = parser.call()
|
|
limit -= 1
|
|
|
|
if limit <= 0: return Result.err(Error.new("executed too many times (%s)" % limit))
|
|
elif len(results) == 0: return Result.err(Error.new("many matched nothing"))
|
|
else: return Result.ok(results)
|
|
|
|
func until(parser: Callable) -> Result:
|
|
return many(
|
|
func ():
|
|
match parser.call():
|
|
var x when x.is_err(): return any()
|
|
_: return Result.err(null)
|
|
)
|
|
|
|
func negate(parser: Callable) -> Result:
|
|
match parser.call():
|
|
var x when x.is_ok(): return Result.err('negated parser passed with value "%s"' % x.unwrap())
|
|
_: return Result.Unit
|
|
|
|
func skip(parser: Callable) -> Result:
|
|
return parser.call().chain(func(_v: Variant) -> Result: return Result.ok(''))
|
|
|
|
func any_char(chars: String) -> Result:
|
|
for ch in chars:
|
|
if self.char(ch).is_ok(): return Result.ok(ch)
|
|
return Result.err(Error.new('expected any of "%s"' % chars))
|
|
|
|
func optional(parser: Callable, default_value: Variant = '') -> Result.Ok:
|
|
match parser.call():
|
|
var x when x.is_ok(): return x
|
|
_: return Result.ok(default_value)
|
|
|
|
func ws() -> Result:
|
|
return any_char(' \t')
|
|
|
|
func skip_ws() -> Result:
|
|
return skip(many.bind(ws))
|
|
|
|
func parse_sign() -> Result:
|
|
return any_char('+-')
|
|
|
|
func parse_digit() -> Result:
|
|
return any_char('1234567890')
|
|
|
|
func parse_digits() -> Result:
|
|
return many(parse_digit).map("".join)
|
|
|
|
func parse_integer() -> Result:
|
|
if self.char('0').is_ok(): return Result.ok(0)
|
|
var sign = optional(parse_sign)
|
|
var digits = parse_digits()
|
|
return _collect([sign, digits])
|
|
|
|
func parse_exponent() -> Result:
|
|
var e = any_char('eE')
|
|
var sign = optional(parse_sign)
|
|
var digits = parse_digits()
|
|
return _collect([e, sign, digits])
|
|
|
|
func parse_fractional() -> Result:
|
|
var point = self.char('.')
|
|
var digits = parse_digits()
|
|
var exp = optional(parse_exponent)
|
|
return _collect([point, digits, exp])
|
|
|
|
func parse_float() -> Result:
|
|
var integer = parse_integer()
|
|
var fractional = optional(parse_fractional)
|
|
return _collect([integer, fractional])
|
|
|
|
func parse_string() -> Result:
|
|
var quote = char.bind('"')
|
|
var slash = self.char.bind('\\')
|
|
var unescaped_quote = all_of.bind([negate.bind(slash), quote])
|
|
|
|
var open = skip(quote)
|
|
var str_value = until(unescaped_quote).map(_join)
|
|
return _collect([open, str_value])
|
|
|
|
class SeqAccess extends Serde.SeqAccess:
|
|
var de: Json.Deserializer
|
|
var first: bool = true
|
|
var parser: Parser:
|
|
get: return de._parser
|
|
|
|
func _init(de: Json.Deserializer) -> void:
|
|
self.de = de
|
|
|
|
func next_element() -> Result:
|
|
parser.skip_ws()
|
|
|
|
if parser.char(']').is_ok():
|
|
return Result.ok(Option.Unit)
|
|
|
|
if not first and parser.char(',').is_err():
|
|
return Result.err(Error.new('expected comma'))
|
|
|
|
first = false
|
|
|
|
parser.skip_ws()
|
|
return de.deserialize_any(Serde.GenericVisitor.new()).map(Option.some)
|
|
|
|
class MapAccess extends Serde.MapAccess:
|
|
var de: Json.Deserializer
|
|
var first: bool = true
|
|
|
|
var iter: PeekableIter:
|
|
get: return de._iter
|
|
|
|
var parser: Parser:
|
|
get: return de._parser
|
|
|
|
func _init(de: Json.Deserializer) -> void:
|
|
self.de = de
|
|
|
|
func next_key() -> Result:
|
|
parser.skip_ws()
|
|
|
|
if iter.peek().filter(Parser.eq.bind('}')).is_some():
|
|
return Result.ok(Option.Unit)
|
|
|
|
if not first:
|
|
var comma = parser.char(',')
|
|
if comma.is_err(): return comma
|
|
|
|
first = false
|
|
parser.skip_ws()
|
|
var key = de.deserialize_any(Serde.GenericVisitor.new()).map(Option.some)
|
|
return key
|
|
|
|
func next_value() -> Result:
|
|
parser.skip_ws()
|
|
|
|
var colon = parser.char(':')
|
|
if colon.is_err(): return colon
|
|
|
|
parser.skip_ws()
|
|
return de.deserialize_any(Serde.GenericVisitor.new()).map(Option.some)
|
|
|
|
class Deserializer extends Serde.Deserializer:
|
|
var _input: String
|
|
var _iter: PeekableIter
|
|
var _parser: Parser
|
|
|
|
func _init(value: String) -> void:
|
|
_input = value
|
|
_iter = ListIterator.new(_input, RangeIterator.new(0, len(_input))).into_peekable()
|
|
_parser = Parser.new(self)
|
|
|
|
static func from_string(value: String) -> Json.Deserializer:
|
|
return Json.Deserializer.new(value)
|
|
|
|
func deserialize_any(visitor: Visitor):
|
|
var next = _iter.peek()
|
|
if next.is_none(): return Result.err(Error.new("eof"))
|
|
match next.unwrap():
|
|
'n': return deserialize_nil(visitor)
|
|
't', 'f': return deserialize_bool(visitor)
|
|
'"': return deserialize_string(visitor)
|
|
'+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
return deserialize_float(visitor)
|
|
'[': return deserialize_seq(visitor)
|
|
'{': return deserialize_dict(visitor)
|
|
var x: return Result.err(Error.new('unexpected character "%s" at position %s' % [x, 0]))
|
|
|
|
func deserialize_nil(visitor: Visitor):
|
|
_parser.skip_ws()
|
|
match _parser.str('null'):
|
|
var x when x.is_ok(): return Result.ok(visitor.visit_nil())
|
|
var x: return x
|
|
|
|
func deserialize_bool(visitor: Visitor):
|
|
_parser.skip_ws()
|
|
return _parser.any_of([_parser.str.bind('true'), _parser.str.bind('false')]).map(visitor.visit_bool)
|
|
|
|
func deserialize_int(visitor: Visitor):
|
|
_parser.skip_ws()
|
|
return _parser.parse_integer().chain(Convert.FromString.to_int).map(visitor.visit_int)
|
|
|
|
func deserialize_float(visitor: Visitor):
|
|
_parser.skip_ws()
|
|
return _parser.parse_float().chain(Convert.FromString.to_float).map(visitor.visit_float)
|
|
|
|
func deserialize_string(visitor: Visitor):
|
|
_parser.skip_ws()
|
|
return _parser.parse_string().map(visitor.visit_string)
|
|
|
|
func _deserialize_complex(open: Callable, close: Callable, on_success: Callable):
|
|
_parser.skip_ws()
|
|
var open_result = open.call()
|
|
if open_result.is_err(): return open_result
|
|
|
|
var value = on_success.call()
|
|
|
|
var close_result = close.call()
|
|
if close_result.is_err(): return close_result
|
|
return Result.ok(value)
|
|
|
|
func deserialize_seq(visitor: Visitor):
|
|
var open = _parser.char.bind('[')
|
|
var close = _parser.char.bind(']')
|
|
var visit = func() -> Variant:
|
|
return visitor.visit_seq(Json.SeqAccess.new(self))
|
|
|
|
return _deserialize_complex(open, close, visit)
|
|
|
|
func deserialize_dict(visitor: Visitor):
|
|
var open = _parser.char.bind('{')
|
|
var close = _parser.char.bind('}')
|
|
var visit = func() -> Variant:
|
|
return visitor.visit_dict(Json.MapAccess.new(self))
|
|
|
|
return _deserialize_complex(open, close, visit)
|
|
|
|
func deserialize_object(_name: StringName, _fields: Array[StringName], visitor: Visitor):
|
|
return deserialize_dict(visitor)
|