signalis-eb/godot/addons/serde/json.gd
2025-06-06 01:49:16 -04:00

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)